Click here to Skip to main content
15,889,281 members
Please Sign up or sign in to vote.
5.00/5 (1 vote)
See more:
Instead of hardcoding 0, there is an elegant Decimal.Zero in .NET Framework. However if you want to assign Decimal.Zero to an integer it involves code like below

C#
Int32.Parse(Decimal.Zero.ToString());

I am trying to see if we can have an extension method on Int32 like below but unable to get it working. What am I missing?

C#
public static int Zero(this Int32 intObject)
        {
            return (0);
        }
Posted
Comments
Sergey Alexandrovich Kryukov 30-May-13 17:01pm    
Nice joke. Congratulations!
First line is especially "elegant". Getting 0, only the way wasting CPU time. And the brain of normal people.
My 5!
—SA
Vasudevan Deepak Kumar 30-May-13 17:19pm    
That appears when we are really idle and trying to kill time, Sergey!

BTW, the intent was to trying to use a bit of extension method magic. Any clues on that front?
Sergey Alexandrovich Kryukov 30-May-13 17:39pm    
Sure. But it's more than that: please see my answer!
—SA
Sergey Alexandrovich Kryukov 30-May-13 17:45pm    
Some fixes added to the text. Please re-load the page.
—SA
tumbledDown2earth 31-May-13 0:32am    
Gawd ... you people really have no work :))

Please consider my answer as a part of this wonderful joke.

First of all, your Int32 extension does work. By some reason, you just failed to see it.

This is the instance method but the extension syntax and mechanism mimic instance method. So, the call should be: int something = 0.Zero(); Or, int somethingElse = 42.Zero(); The instance itself makes no difference, but it's just because you don't use "this intObject" parameter.

As I think you are joking, you understand how absurd the idea is. And this is not only because you have to use integer object anyway, to have some (unused) instance. It's worse. Of course, adding this "functionality" just to one integer type would not make much sense; we need to cover all types. Let's start with two:

C#
namespace System { // note the elegance and convenience of using this namespace; this is quite a legitimate thing
    public static class SystemExtensions {
            public static int Zero(this int unusedValue) {
                return 0;
            }
            public static uint Zero(this uint unusedValue) {
                return 0;
            }
    } // class SystemExtensions
} // namespace System

So far so good, but do you see the ambiguity? Let's try:
C#
int a = 0.Zero();
uint b = 0.Zero(); // this line won't compile!

Even though, to some, it looks apparent that in the second case, public static uint Zero(this uint unusedValue) is implied, this is not apparent to the compiler. :-)
Here is the correct work-around:
C#
int a = ((int)0).Zero();
uint b = ((uint)0).Zero(); // great laconic syntax to write 0, isn't it? :-)

With every step, more and more "elegance", right? :-)

And I already discussed the "elegance" of the very first line shown in the question. By the way, the work-around is really simple: you should simply define all possible conversion methods for zeroes of all numeric types, in the same extension-method way. Obvious, isn't it?

However, wouldn't another thing be even simpler? This:
C#
namespace System {
    public static class SystemExtensions {
        public static int IntZero = 0;
        public static uint UIntZero = 0;
        public static Int16 Int16Zero = 0;
        public static UInt16 UInt16Zero = 0;
        public static Int64 Int64Zero = 0;
        public static UInt64 UInt64Zero = 0;
        //...
    } // class SystemExtensions
} // namespace System


[EDIT]

Even better, just the immediate usage:
C#
ulong value = default(ulong); // inspired by Andreas!
[END EDIT]

By the way, even considering such an off-the-scale amount of absurdities, I think it as an opportunity to learn something real.

Thank you for so much fun! :-)

—SA
 
Share this answer
 
v8
Comments
Andreas Gieriet 30-May-13 18:05pm    
My 5, Sergey!
A shorter way to get the absurdity implemented for all types ;-):
public static class Extension
{
public static T Zero<T>(this T obj) { return default(T); }
}
...
Console.WriteLine("Zero = {0}", 42.Zero());
Console.WriteLine("Zero = {0}", DateTime.Now.Zero());
Console.WriteLine("Zero = {0}", "abc".Zero())

Cheers
Andi
Sergey Alexandrovich Kryukov 30-May-13 19:00pm    
Thank you, Andi.

Very good point.
And look at the usage in this numeric case:
double value = 0d.Zero<double>();

What the ikebana! :-)

By the way, it gave me the idea to add some [EDIT] to the answer, but on the sobering section of it; please see :-)

—SA
Andreas Gieriet 31-May-13 6:17am    
I guess default(...) is even evaluated at compile time for statically know types. But then, why not writing 0 or null. default(...) is useful in generic functions (like in LINQ), but odd in non-generic code. I consider it kind of a code bloat ;-)
Cheers
Andi
Sergey Alexandrovich Kryukov 31-May-13 7:47am    
Hard to say... Getting rid of all immediate constants has its value. I usually allow myself just writing 0 and 1 as immediate constants, but imaging that you are exterminating all immediate constants to fix some legacy code. You move all such constants in separate files and then check it up: using editor's Regex you make sure that other files contain no digits at all. How about that?
Anyway, despite of somewhat paranoid character of out discussion, there is something to think about...
—SA
Andreas Gieriet 31-May-13 9:20am    
Agreed. Getting rid of immediate literals is at least desirable (e.g. should be part of each codeing guideline). E.g. I strongly prefer having defined a constant like int PollTimeoutInMilliseconds = 5000; over using the plain number in a Sleep(...) call. However, I view some idiomatic use of immediate literals as allowed. E.g. for(int i = 0; i < table.size(); ++i) {...} or while(item != null) {...}, etc. Basically: 0 and null are in most cases the simplest and best solution. Defining a constant Zero is a bit odd in my eyes since everyone assumes "Zero" to be the equivalent of 0 of that type, so why not using the plain literal for that - this is well understood by everyone and you are not likely to change constant Zero to something else that "0". If one is tempted to define a constant with name "Zero" or "One", then he might ask himself if "Zero" is the right name for that - maybe "Empty" or "Sentry" or "Neutral" or alike would better match the particular intent then). E.g. for(int i = default(int); i < s.Length; ++i) {...} or for(int i = Zero; i < s.Length; ++i) {...} looks really paranoid to me.
Cheers
Andi
As alternative to the the absurdity of employing extension methods for this (e.g. 42.Zero()) as Sergey mentioned in solution #2, you might use default(T).
E.g.
C#
Console.WriteLine("Zero = {0}", default(int));
Console.WriteLine("Zero = {0}", default(DateTime));
Console.WriteLine("Zero = {0}", default(string));

If you insist in your Zero name, you might wrap it into a class, e.g.
C#
public abstract class Zero
{
    public static T Value<T>() { return default(T); }
}
...
Console.WriteLine("Zero = {0}", Zero.Value<int>());
Console.WriteLine("Zero = {0}", Zero.Value<DateTime>());
Console.WriteLine("Zero = {0}", Zero.Value<string>());

Or if you prefer a slightly alternative Zero class:
C#
public abstract class Zero<T>
{
    public static T Value { get { return default(T); } }
}
...
Console.WriteLine("Zero = {0}", Zero<int>.Value);
Console.WriteLine("Zero = {0}", Zero<DateTime>.Value);
Console.WriteLine("Zero = {0}", Zero<string>.Value);


Have fun!
Cheers
Andi

PS: C# does not allow to define extenstion methods for a type (e.g. Int32.Zero). You only can define extension methods on objects of a type (e.g. 42.Zero()). Maybe this is your confusion.
 
Share this answer
 
v4
Comments
Sergey Alexandrovich Kryukov 30-May-13 19:02pm    
Sure, a 5.
—SA
Vasudevan Deepak Kumar 30-May-13 21:16pm    
But how about Decimal.Zero? Decimal is also a type right?
Sergey Alexandrovich Kryukov 31-May-13 1:34am    
Will work, too. Everything will work with this generic. Don't even try.
—SA
Andreas Gieriet 31-May-13 5:32am    
The decimal class seems to have three explicitly predefined symbolic values:
- decimal.MinusOne
- decimal.Zero
- decimal.One
There is no good reason given why these three items exist. The other types do not provide these symbols.
There is some guessing why they are there...
Cheers
Andi
Sergey Alexandrovich Kryukov 31-May-13 8:01am    
I welcome Zero and One, but not MinisOne because this is -decimal.One...
0 and 1 are fundamental in algebra (1 is a 0 for multiplicative operations, do you know such things..?) and... in loops, sometimes...
—SA
One more variant, based on implicit numeric type conversions. Let's consider this:
C#
public static class Generic {
    public const byte Zero = 0;
    public const sbyte SignedZero = 0; // will be needed on in one case
} //Generic

Apparently, as all other numeric types, excluding sbyte are wider then byte, it works:
C#
uint ui = Generic.Zero;
int i = Generic.Zero;
long l = Generic.Zero;
ulong ul = Generic.Zero;
short sh = Generic.Zero;
ushort ush = Generic.Zero;
decimal d = Generic.Zero;
double dbl = Generic.Zero;
Single sng = Generic.Zero;
byte b = Generic.Zero;
// the only special case:
sbyte sb = Generic.SignedZero;

Naturally only byte and sbyte are incompatible without explicit conversion, but all other numeric type are compatible with both byte and sbyte.

If such asymmetric schema (not fair to either sbyte or byte :-)) seems a bit ugly, another option is to distribute the universal constants equally between signed and unsigned types:
C#
public static class Signed {
    public const sbyte Zero = 0;
} // Signed
public static class Unsigned {
    public const byte Zero = 0;
} // Unsigned

In this case, if all signed types (including both floating-point) use Signed.Zero, and all unsigned types use Unsigned.Zero, it will work. Again, it looks somewhat redundant, because all types weider then byte and sbyte can use either variant.

Is it good or bad? The problem is some performance cost related to implicit conversion. This is a runtime operation, so the performance will be not as good as with same-type constant or just with immediate constant zero, which are known at compile time. (In some languages, such as Borland Pascal or Deplhi Pascal, there are "real" generic constants. Types are not declared, and the compiler generates real immediate constant depending on the type of target object. C and C++ have "#define", which is an apparent lame.)

What's better? Still, default(TYPE). All out inventions should be considered with some humor, even though we touch serious aspects of programming.

[EDIT]

I almost forgot: it's also good to have the definition for One, to make sure that we can completely get rid of all immediate constants in any code. All other values can be made explicitly declared constants and put in some special files with definitions, or even in data files and resource.

Also, good to mention one similar issue: null should be considered acceptable (by the way, the scheme shown above will also cover nullable numeric types (like int?). As to "", it should be exterminated first. The only acceptable alternative is string.Empty.

Note that Solution 3, as it is shown in its text, covers all types at once, but string.Empty is still needed as default(string) is of course null.

—SA
 
Share this answer
 
v2
Comments
Andreas Gieriet 31-May-13 5:42am    
My 5 again.
For the elaboration on the implicit type conversion you might add the following references: Implicit Numeric Conversions Table and Explicit Numeric Conversions Table.

See also my comment in solution #3 on the explicitly provided symbolic values decimal.MinusOne, decimal.Zero, decimal.One.

Cheers
Andi
Andreas Gieriet 31-May-13 6:11am    
The issue about specialized "default" values other than the default(T) falls into the category "missing decent support for generics specialization". See How to do template specialization in C#.
Applying this to the Extension class of my comment in solution #2:
public static class Extension
{
public static T Zero<T>(this T obj) { return default(T); }
public static string Zero(this string obj) { return string.Empty; }
}

Unfortunately, this "specialization" does not extend to usage in generic functions. This does not work as expected, i.e. it always calls the generic method :-(
public abstract class Zero
{
public static T Value<T>() { return default(T).Zero(); }
}

I consider this a major limitation of C# generics: generic methods do not take the best match when calling a method, but always the generic function.
For the fun of it, you can work around this limitation by doing the following:
public abstract class Zero
{
public static T Value<T>() { return Extension.Zero((dynamic)default(T)); }
}

Much ado about nothing! :-)
Cheers
Andi
Sergey Alexandrovich Kryukov 31-May-13 7:58am    
You are absolutely right.

Let me tell you: the root of the problem is that I can see just 2-3 big failures of .NET, one of them is related to generic specialization, but where is it stemmed from? From the decline of the idea to inherit primitive types (and also enumerations and structures). Due to this, its nearly impossible to create a generic type/method abstracted from a concrete numeric type, in particular. (And the C++-like templates were of course not an option.)

Generics aside, think about it: you cannot define, say, "type Size = single" in one file and then use the type Size in calculations in other files (like in typedef, at least); in this case you could switch to double for Size type in all project by modifying just one line. And where is the reuse?

—SA
Andreas Gieriet 31-May-13 9:39am    
Yes, Generics are more limited than necessary (mainly specialization is missing - this would probably solve most of the follow-up problems with generics).

And missing "typedef", I agree (you can define an alias in the *client* code (e.g. using Vector = List<int>;), but not in the library code - alias are not part of the assembly interface).

What I also miss is useful RAII. IDisposable is not a good solution for that since you depend on the client of your code to respect the contract. Such a to-be-invented type might have some constraints like will always have reference count 1, etc.

So, back to topic again. I think we elaborated enough on the original post ;-)
Sergey Alexandrovich Kryukov 31-May-13 10:00am    
They are not, but the point is: if it was not typedef but type by inheritance (like in Ada supporting the notion of type and subtype, for all types), it would be the part if CIL.

Now, as to RAII: who do you think provided the alternative to my article? You did:
http://www.codeproject.com/Tips/140137/Hourglass-Mouse-Cursor-Always-Changes-Back-to-its

I really like it.

But now, as you provided a good RAII implementation, isn't it so important to have it in .NET BCL, don't you think so?

It could go to far. Design patterns do not belong in BCL, I think, it would bloat it, don't you think so? And there a more fundamental things missing, those which could not be resolved at the level of just another library.

Likewise, I was frustrated by the fact that enumeration types do not enumerate. (In fact, I think this is also a fallacy, too, but relatively minor or; in Object Pascal and Ada enumeration types do inherently enumerate.)
Did you see my article where I provided a workaround at the level of the library code.

What is really fundamental though is the lack of meta-classes. I don't know why Anders Hejlsberg, who was the Borland Pascal architect, did not accomplish the Borland-like meta-classes in .NET. .NET reflection is almost there, many other Borland ideas are already in .NET, so why not perfecting its architecture just a bit further? Are you familiar with those meta-classes? They are really powerful. Now we have only System.Type. A meta-class is the class as an object...

—SA
Oh, no. There is yet another option, based on MinValue constants. It's enough to demonstrate the immediate usage:
C#
uint ui = uint.MinValue;
int i = byte.MinValue; // int.MinValue is not null, of course
double dbl = byte.MinValue; // double.MinValue is not null, of course
// and so on...

With types having non-zero minima, it presents the same very minor performance problem as in Solution 4.

—SA
 
Share this answer
 
Comments
Andreas Gieriet 31-May-13 6:22am    
How about
int i = int.MinValue - int.MinValue.
Zero again! :-)
Cheers
Andi
Sergey Alexandrovich Kryukov 31-May-13 7:39am    
Sure... :-)
—SA

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900