7

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.

Ben
  • 1,272
  • 13
  • 28
  • I don't think the second code is ugly at all, personally. – Ry- Jun 19 '12 at 19:09
  • 3
    I did, when I first started writing code this way. But I got used to it in about 5 minutes. – Robert Harvey Jun 19 '12 at 19:10
  • I updated my answer, because I think all of our first answers were sub-standard. IMHO, NONE of those Math functions should be in the call to the Vector3 function. Each of those calculations should be made, and stored, and given better variable names to help make the code easier to read. – CaffGeek Jun 19 '12 at 19:26
  • As a programmatic statement, I agree with @RobertHarvey. As a representation of a mathematical equation, the C# statement is clearly ugly and less intelligible than the VB.NET statement. – Ben Jun 19 '12 at 19:34

7 Answers7

12

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.

jcolebrand
  • 15,889
  • 12
  • 75
  • 121
  • 2
    While this does answer the user's question, I would strongly discourage using this. This is much LESS readable than writing out the class name in full. – Chris Kerekes Jun 19 '12 at 19:20
  • I agree with Chris, this is much less readable, and it would have been good of me to point that out before he showed up and mentioned it. This is the way to do what was asked for, but "those of us who have been doing this a while" really prefer to see `Math.Sin(...` in our codebase, and `using X = Library.Namespace.Thing;` can be annoying when trying to track something down. +1 Chris. – jcolebrand Jun 19 '12 at 19:23
  • I understand the concern about masking where Sin and Cos are coming from. But in this case, I think it's worth sacrificing that type of clarity to reduce the useless characters in the equation making it more readable as a mathematical equation (as opposed to a programmatic statement). It's not necessary to remind the developer 3 times in one line that Cos comes from Math. – Ben Jun 19 '12 at 19:33
  • 1
    @Ben, I suggest that you take a capital `M` to differentiate it from variable names. – Lucero Jun 19 '12 at 19:35
  • @Lucero does `M` vs `m` make any difference? I did it out of laziness, which I have to assume anyone employing this method is _also_ going to want to be. Consider that if he didn't want to be lazy, he wouldn't have asked this in the first place. – jcolebrand Jun 19 '12 at 19:37
  • @jcolebrand, yes it does for readability IMHO, since lowecase is (obviously) used for local variables and parameters here. Using the capital `M` helps the human parser. – Lucero Jun 19 '12 at 19:40
  • 2
    I've worked with some, but anyways, how would that make a difference? It's about code and code conventions. Even if it is an alias to a class, it still is a class name. And those are uppercase in the common code conventions (heck, even JavaScript "constructors" are written in uppercase for readability reasons). – Lucero Jun 19 '12 at 19:44
  • Because they're lazy (at least, the dozen or so I know are, and they're rather proud of being that lazy). They're not looking for the purity of the art, and if OP was, he would use `Math.Sin` instead of `M.Sin`. You're literally arguing about the smallest minutiae possible. Let it go. Breath. I considered both ways before I ever hit enter, and edited my post several times. I could've easily made it `M` instead of `m` and used my years of experience to determine that in this case, `m` was more correct. Subjective Objectivity ftw. – jcolebrand Jun 19 '12 at 19:46
  • 2
    @jcolebrand: I agree with Lucero here. `m.Sin(...)` *looks* like it's calling an instance method on something. The .NET naming conventions are extremely clear about this - type names start with capital letters. I don't see why that convention should go out of the window just because we're aliasing it. – Jon Skeet Jun 19 '12 at 19:52
  • Because the asker who doesn't want to write out three more letters doesn't care about capitalization. That is all I'm thinking here. I wouldn't even shorten it, but I'm familiar with how the framework works enough to answer this question. It's just an alias. I personally would suggest always writing out `Math` so that it's more correct. – jcolebrand Jun 19 '12 at 19:54
  • @Ben I don't mean you when I say "the asker" above, I mean anybody who reads this thread thinking "oh, I should do that". They make a good point, but if you're going for faster typing, and you're the only one maintaining it, using a lowercase m can feel faster, and look consistent throughout, but capital M does conform to the conventions everyone else uses. – jcolebrand Jun 19 '12 at 19:55
  • 2
    @jcolebrand, don't worry about me, I'm breathing alright. I made a suggestion to Ben (and others seem to agree on my point) because it felt like the right thing to do, and because you asked I explained why. That's all... *shrug* - Edit: and about the part of lazyness, it is one character either way, how would uppercase be less lazy? I'm all for being lazy - following conventions is just that because it reduces the need for thinking (which is what I meant with making it easier for the human parser). – Lucero Jun 19 '12 at 19:59
9

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.

Community
  • 1
  • 1
vcsjones
  • 138,677
  • 31
  • 291
  • 286
  • Thanks, this is a great answer too even though I'll probably accept jcolebrand's answer. I didn't know that the JIT would inline trivial functions like I was mentioning; I'll have to look into that. This code will be performance-sensitive, so the difference of function call overhead is material. The white space is also a good comment, although I already have it in my real code (which is considerably more complex than this example). – Ben Jun 19 '12 at 19:24
  • `using static System.Math` doesn't seem to work for constants like `PI`, though the methods work as expected. I still have to write `Math.PI` or the code doesn't compile. Anyone know why this is? Something special about the constants? – skwear Sep 22 '16 at 20:12
7

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.

CaffGeek
  • 21,856
  • 17
  • 100
  • 184
  • I can't upvote twice, but I will say that "parm1" is a terrible name ... also, I agree with your point of it not looking as pretty, but that's life. – jcolebrand Jun 19 '12 at 20:12
  • 3
    I realize parm1 is a terrible name, but I don't know the domain well enough to give it a proper one...hence the comment. – CaffGeek Jun 19 '12 at 20:25
7

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.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I'm curious the thought on just using intermediary values that actually reflect intent even tho it's a waste (to a programmer) of space because of variable declarations. I'm willing to bet a one-time assignment like that might get compiled out by a smart compiler... – jcolebrand Dec 03 '12 at 16:40
  • @jcolebrand: It's more likely to be optimized by the JIT than the C# compiler, but yes - I'm totally happy using extra variables for readability. – Jon Skeet Dec 03 '12 at 16:41
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.

Lucero
  • 59,176
  • 9
  • 122
  • 152
  • You're still adding something _in front of_ `Sin()` – jcolebrand Jun 19 '12 at 19:13
  • Not really, I'm moving the argument to the front. Only 1 character (the `.`) is added really to the length, which is the best so far here... ;) – Lucero Jun 19 '12 at 19:14
  • +1. @jcolebrand, That's why I've said unnatural in my semi-identical answer :). – Alexei Levenkov Jun 19 '12 at 19:15
  • I believe my method is shorter in overall characters than yours. Also, re-read the VB example. – jcolebrand Jun 19 '12 at 19:15
  • @jcolebrand, that depends on whether you count the extension method code and if so how many times the methods are used. If the app has plenty of those calls then mine should be less. ;) – Lucero Jun 19 '12 at 19:17
  • An interesting approach; I like it :) But, I think it's a little less immediately readable for someone expecting functions to operate on variables rather than invoking functions as methods of variables so I'll probably go with the alias shortening above. – Ben Jun 19 '12 at 19:27
  • @Ben, it makes it look like a method of the `double`, which arguably is more natural since you also use methods on other "simple" objects such as `string` or `DateTime` for doing computations. But of course it may be a bit unexpected for people with programming experience or people expecting a traditional math expression. – Lucero Jun 19 '12 at 19:31
  • @Lucero, I agree on all counts. In this case, I think the "looking like a math expression" probably tips the scales the other way, but just barely. Thanks for suggesting this method -- I'm sure I'll use it in other cases. – Ben Jun 19 '12 at 19:40
1

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(),....
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
  • If you made the extension method in the class you were in you could get away without having to call the container, and I would've _said_ extension method in mine, but I forgot the name of it (knew how it was written, forgot the name, in the moment of answering) – jcolebrand Jun 19 '12 at 19:17
0

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

Ribtoks
  • 6,634
  • 1
  • 25
  • 37
  • Well, you could with a delegate, I believe, but that's just a LOT more boilerplate code for not a lot of benefit, and it's still limited to being used just in that class. – jcolebrand Jun 19 '12 at 19:14
  • @jcolebrand yes, but I don't think, that author will define 100500 delegates to every Math method he uses – Ribtoks Jun 19 '12 at 19:15