Can Math references be shortened in C#?

2019-04-26 21:42发布

问题:

In VB.NET, I could make my math code a lot cleaner by Importing System.Math and referencing its methods directly:

Imports System.Math
[...]
Return New Vector3(Sin(az) * Cos(el), Cos(az) * Cos(el), Sin(el))

But I don't think C# can Use classes in order to access their methods implicitly, so I have to do something like:

using System;
[...]
return new Vector3(Math.Sin(az) * Math.Cos(el), Math.Cos(az) * Math.Cos(el), Math.Sin(el));

But that's ugly; it needs its own scroll bar!. Is there any way to write something in C# that looks like my VB.NET code? I could write local wrapper methods for Sin and Cos, but wouldn't that reduce performance (because of the overhead of function calls)? And, that would require writing wrapper functions for every Math function I'm using in every class I'm using them in; that's not so desirable either.

回答1:

You could give System.Math an alias with the using directive:

using M = System.Math;

return new Vector3(M.Sin(az) * M.Cos(el), M.Cos(az) * M.Cos(el), M.Sin(el));

That's the best I got.

I could write local wrapper methods for Sin and Cos, but wouldn't that reduce performance (because of the overhead of function calls)?

You could, and at that point you could use generics and other niceties to make it even more convenient, but you would still be referencing a library like this, unless all the math was happening in the same class where this code is occuring as methods.

The question of "performance from overhead" is answered with "additional stack frames for something this simple are non-noticeable for standard applications". If you were in a tight loop, then yes, that would be an issue.



回答2:

As of C# 6.0 you can shorten the math references by adding a using static declaration:

using static System.Math;

This allows you to use static members of the Math type without qualifying it with the type name:

public void Foo()
{
     var bar = Sin(8);
}

There is no way to do this "globally" since there is no way to apply a using declaration globally at all, currently.

For those not using C# 6

but wouldn't that reduce performance (because of the overhead of function calls)?

I wouldn't worry about it. Write your code to be readable first. We can write the method a few different ways. The first, is to just add white space:

return new Vector3(
    Math.Sin(az) * Math.Cos(el),
    Math.Cos(az) * Math.Cos(el),
    Math.Sin(el)
);

You can also put that in a helper method, too.

jcolebrand's answer is a nice was of doing that too, but it requires adding a using m = System.Math; everywhere.



回答3:

But that's ugly; it needs its own scroll bar!

Yes, on Stack Overflow, which has a short width. You have to put it on multiple lines instead:

return new Vector3(Math.Sin(az) * Math.Cos(el),
                   Math.Cos(az) * Math.Cos(el),
                   Math.Sin(el));

Personally I think that's more readable anyway, because now the three different values are more clearly differentiated. A blank line is a clearer separator than just a comma, IMO. When you've got several arguments which are sufficiently similar that they can get lost together, use multiple lines for clarity.

Yes, it would in some ways be nice if you could just write Sin(az) etc - but there's nothing in C# that will let you do that, so I'd stick to reformatting the code for readability instead.



回答4:

You can add

using M = System.Math;

and then just do

return new Vector3(M.Sin(az) * M.Cos(el), M.Cos(az) * M.Cos(el), M.Sin(el));

but....I don't see the real value

In fact, I think each of those variables should be stored, with better names to represent what they are. Looking at this, I don't see what is being done.

var sinAz = Math.Sin(az); //Could these be named better?  Perhaps with words in the correct domain 
var cosAz = Math.Cos(az);
var sinEl = Math.Sin(el);
var cosEl = Math.Cos(el);

return new Vector3(sinAz * cosEl, cosAz * cosEl, sinEl);

Or better, what are the three parameters that are used by Vector3 actually called?

var parm1 = Math.Sin(Az) * Math.Cos(El);  //What should parm1, parm2, parm3 be called?
var parm2 = Math.Cos(Az) * Math.Cos(El);
var parm3 = Math.Sin(Az);

return new Vector3(parm1, parm2, parm3);

That would GREATLY improve readability as the next person would have a better understanding of what the formula was.



回答5:

You can create a class that wraps the calls as extension methods:

public static class MathExtensions {
    public static double Sin(this double value) {
        return Math.Sin(value);
    }
    public static double Cos(this double value) {
        return Math.Cos(value);
    }
}

return new Vector3(az.Sin() * el.Cos(), az.Cos() * el.Cos(), el.Sin());

The compiler should inline those calls so that the performance shouldn't differ really.



回答6:

You can also use extension methods, but result will look more unnatural (with potential extra function call, also it likely to be inlined away):

static double Sin(this double value) { return Math.Sin(value);}

And use:

return new Vector3(az.Sin() * el.Cos(),....


回答7:

You cannot define an alias to method in C#, just to a class.