2

To make it short, I've got a method which look like this

public T GetCookie<T>(string key)
    {
        T obj;

        if (typeof(T) == typeof(int))
        {
            return -1;
        }
        else
        {
            return default(T);
        }
    }

Why am I doing this ? The default value of an int is 0, and i need it to be -1 (I got a huge code sample based on that -1), so i was wondering if something exists that could change the default value of an int, or allow me to return an int instead of my generic class.

I didn't find anything for now :/

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
Guillaume Munsch
  • 1,233
  • 17
  • 37
  • No, and for good reason, you've found out that its the default, how would you like to discover a bug because some developer on your team changed it? – Sayse Jun 05 '15 at 07:05

3 Answers3

2

Try doing something like:

return (T)(object)-1;
xanatos
  • 109,618
  • 12
  • 197
  • 280
  • It's compiling ! I'm gonna make some tests ;) Ty ! – Guillaume Munsch Jun 05 '15 at 07:07
  • Yes, that's what I usually do as well. That said, I don't really like it, because it implies a box/unbox. Would be nice if there was something better - that said, I haven't found a better solution yet. – atlaste Jun 05 '15 at 07:07
  • @atlaste If you have a fixed value, you can cache it in a generic struct... Otherwise you can play with `Expression` trees and auto-generate some code. See the first example in http://stackoverflow.com/a/18182340/613130 – xanatos Jun 05 '15 at 07:09
  • @xanatos The reason I don't like it is not because of performance, but because of type safety. What I want is basically a C++ template specialization, but I know that's impossible. In most cases you can use a `dynamic` and overload resolution rules, which is the next best thing I suppose -- but that also breaks type safety. – atlaste Jun 05 '15 at 07:13
  • @atlaste There is no way to do it at compile time... Technically you could do something with `Fody`, to generate errors at "post compile time", or by writing a custom rule with Code Analysis. – xanatos Jun 05 '15 at 07:14
  • why not ``return (int)-1;`` as it is already in if block for int Type – Ehsan Sajjad Jun 05 '15 at 07:17
  • @EhsanSajjad Because it doesn't work that way. http://goo.gl/j0qGgt (see that the `long` with the double cast works, while the `int` doesn't). C# doesn't consider the `if` to restrict the type of `T`. – xanatos Jun 05 '15 at 07:18
  • @xanatos I've added a bit of code on how I usually solve this. Because the correct overload for `default()` can be determined compile time for fixed types, it works... That said, it's not ideal. – atlaste Jun 05 '15 at 07:34
2

No, there is no way to change the default value. It is the value that is assigned by the compiler when initializing fields.

A solution for your problem would be purely opinion based.

You could have separate methods if only a limited amount of types are used:

public int GetIntCookie(string key)

public string GetStringCookie<T>(string key)

public DateTime GetDateTimeCookie<T>(string key)

Or you could maintain a dictionary of default values:

private static Dictionary<Type, object> = new Dictionary<Type, object>
{
  { typeof(int), -1 },
  { typeof(string), string.Empty },
  { typeof(DateTime), DateTime.MinValue },
}
public T GetCookie<T>(string key)
{
    object value;
    if (defaultValues.TryGetValue(typeof(T), out value)
    {
      return (T)value;
    }
    return default(T);
}

Or you could keep your implementation (and fix the compiler error)

public T GetCookie<T>(string key)
{
    if (typeof(T) == typeof(int))
    {
        return (T)(object)-1;
    }
    return default(T);
}
Stefan Steinegger
  • 63,782
  • 15
  • 129
  • 193
  • *A solution for your problem would be purely opinion based.* No, the solution isn't opinion based. Choosing the solution is opinion based. The possible solutions are language based. He didn't ask "what is the best way to do". He asked "how can I do it" – xanatos Jun 05 '15 at 07:38
1

As I mentioned in the comments, I always prefer a scenario that uses type safety over one that does not. What you would actually like to do is this:

public T GetCookie<T>(string key)
{
    return default(T);
}

public int GetCookie<int>(string key)
{
    return -1;
}

... unfortunately that doesn't work in C# (for very good reasons).

If you need to return a string, the solution from @xanatos is the only right solution IMO.

However, if you don't care to pass along another argument, the next best thing is to use overload resolution to pick the correct member:

class SimpleOverloadTest
{
    private static T GetCookie<T>(string key, T value)
    {
        return value;
    }

    private static int GetCookie(string key, int value)
    {
        return -1;
    }

    static void Main()
    {
        Console.WriteLine(GetCookie("foo", default(int)));
        Console.WriteLine(GetCookie("foo", default(float)));
        Console.ReadLine();
    }
}

Note that this WILL NOT work if you add something like this:

private static T GetCookie<T>(string key)
{
    return GetCookie(key, default(T));
}

The reason for this is that the overload should be determined compile-time, and simply put - in this case, the overload that is picked is the overload with the generic argument -- the wrong one in our case.

Generic specialization

I'll interpret the question from @xanatos as a "why does C# not support generic specializations?". I've read about it in an article from the compiler team a long long time ago, but cannot find it anymore. So I'll do my best from the top of my head... People like Eric Lippert probably can give a much better answer than me about this subject though... Here goes:

Generic types aren't expanded at C# compile time. A method is compiler as a method, with a unique token; you basically call a method token. This is one of the foundations of .NET since the very beginning: you compile code per-method. It also means that overload resolution can happen at 'C# compile time', not JIT time, making the JIT much faster.

The choice of not supporting generic specializations in .NET was I think mainly a choice of (runtime / JIT / compiler) speed.

If we would implement generic overloading, we would have to store multiple method implementations for each method token during C# compile time, which would break the foundation mentioned above. Then, you can compile each set of methods with the same method token to different (assembler) implementations. At this point, you have to realize that this will put a lot of stress on the JIT'ter: the JIT has to perform some sort of overload resolution to pick the 'right' method; the C# compiler can help the JIT'ter with that, because it will simply generate the methods. This alone is already a good reason not to do this - but let's continue:

Ironically, template expansion is basically how it works in the JIT. If you have a value type, the value type is put into the item 'T', and the method is 'really' expanded to a different piece of IL and assembler. In other words: if you have Foo<T>, then Foo<int> will have different IL and assembler code than Foo<double>. This is not the case for reference types, as the same vtable lookup (of the interface constraint) can be made for all calls. In other words, the code is shared for reference types, it's not for value types. You can actually see this happen here.

By doing it like this, your JIT is able to produce very good code very quickly. Since a lot of generics are reference types, it also benefits greatly from the generalization rule, which makes it bleeding fast. However, if you would specialize reference types, it would break this optimization - again, a good reason not to do this.

So, this leaves us with generic specialization on value types alone. At this point, you have to realize that value types can be composed of other value types, which means that we can recursively compose types. In C++ we call these things type-lists (google for: template meta programming), which are expanded compile-time. In C# this would mean (because of the above) that type-lists are expanded during the JIT phase, and probably inlining takes place because value types don't support inheritance. This would put an enormous stress on the JIT compiler, and would also make it much more complex to implement.

To conclude, yes, this was a choice, but I believe it's a well balanced one based on performance and complexity of the JIT if this feature were added.

So we now know why generic specializations aren't supported in .NET. An alternative, much easier, solution is however possible: Just like async and yield don't use continuations, you can also use f.ex. a Dictionary or a dynamic to do the actual overload resolution without breaking anything. This wouldn't break .NET, it would just introduce some helper methods / classes. I guess you can propose it as a feature request of course on MS Connect.

Community
  • 1
  • 1
atlaste
  • 30,418
  • 3
  • 57
  • 87
  • *for very good reasons* What should these good reasons be? The missing feature of "generics specialization" (that exists in C++ as (partial) template specialization) is simply a choice Microsoft did in .NET 2.0 . – xanatos Jun 05 '15 at 08:01
  • 1
    @xanatos I did my best to answer that, hope I didn't make huge mistakes. – atlaste Jun 05 '15 at 08:57
  • It is what I would have wrote :-) (but my english would have been a lot worse). In the end they chose to optimize the reference case for generics, and this made it much more difficult to do generic specialization, late binding of methods used in generic methods and so on. They surely fought a lot about this (because it is a big decision, and both choice were good). I don't have the data they surely had, so I can't know if it was the best choice. I can only trust them :-) And surely it is the easiest to use reflection-wise... Having generic specialization would have made very complex reflecting – xanatos Jun 05 '15 at 09:04