Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overloading generic type parameters disallowed?

It's part curiosity and part because I was just trying to use this. If you have the following definitions, this is not allowed by the compiler because it says that the member is already defined. What's the reasoning behind not allowing exclusive overloads of generic type parameters?

void Get<T>() where T: struct {}
void Get<T>() where T: class {}

It seems to me that there's no inherent problem with this. One might argue that it is not always clear which the compiler should choose in cases where the definitions overlap (but a common resolution seems to be most specific match first).

Can someone help me understand or point to a resource what the reasoning is behind disallowing this?

like image 312
Abel Avatar asked Mar 08 '12 15:03

Abel


People also ask

Can generic methods be overloaded?

A generic method can also be overloaded by nongeneric methods. When the compiler encounters a method call, it searches for the method declaration that best matches the method name and the argument types specified in the call—an error occurs if two or more overloaded methods both could be considered best ...

What Cannot be Parameterized with a type using generics?

Correct Option: C. Cannot Overload a Method Where the Formal Parameter Types of Each Overload Erase to the Same Raw Type.

What are generic constraints in C#?

The where clause in a generic definition specifies constraints on the types that are used as arguments for type parameters in a generic type, method, delegate, or local function. Constraints can specify interfaces, base classes, or require a generic type to be a reference, value, or unmanaged type.

How many type parameters can be used in a generic class?

You can also use more than one type parameter in generics in Java, you just need to pass specify another type parameter in the angle brackets separated by comma.


2 Answers

Eric Lippert already answered this one, in a blog post on generic constraints and method signatures: http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx

Constraints on generic types are not part of method signatures in the CLR, therefore you can't have two methods that differ only in generic type constraints. Without CLR support it would be quite fiddly to get C# to support these in a sensible fashion that is compatible with other .NET languages.

like image 76
thecoop Avatar answered Sep 27 '22 23:09

thecoop


The struct constraint on Nullable<T> is IMHO really unfortunate. Something like a Nullable<String> or a Nullable<Nullable<Nullable<int>>> might be have wasteful, but so what? Box the former as its content; unbox it as its content and set the HasValue if the content is non-null. Box the former as an int if all of the nullables report HasValue, and when unboxing, set HasValue of all nested items if the content was non-null.

Otherwise, I would suggest that you create a static generic class with type parameter T which contains a delegate property that accepts a T as a parameter. The property should return the content of a private field which should be initialized to point to a method that will check the type of T and set the delegate to either the struct or class version as appropriate.

Here's a sample of what I'm talking about; this one uses various interface constraints rather than struct/class constraints, but the same principles can be used just as effectively.

        static class _FooDispatcher<T>
        {
            public static Action<T> Foo = setupFoo;

            static void doFooWithIGoodFoo<TT>(TT param) where TT : IGoodFoo
            {
                Console.WriteLine("Dispatching as IGoodFoo with {1} type {0}", typeof(TT).ToString(), typeof(TT).IsValueType ? "value" : "reference");
                param.Foo();
            }
            static void doFooWithIOkayFoo<TT>(TT param) where TT : IOkayFoo
            {
                Console.WriteLine("Dispatching as IOkayFoo with {1} type {0}", typeof(TT).ToString(), typeof(TT).IsValueType ? "value" : "reference");
                param.Foo();
            }
            static void doFooSomehow<TT>(TT param)
            {
                Console.WriteLine("Nothing exciting with {1} type {0}", typeof(TT).ToString(), typeof(TT).IsValueType ? "value" : "reference");
            }
            static void setupFoo(T param)
            {
                System.Reflection.MethodInfo mi;
                if (typeof(IGoodFoo).IsAssignableFrom(typeof(T)))
                    mi = typeof(_FooDispatcher<T>).GetMethod("doFooWithIGoodFoo", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
                else if (typeof(IOkayFoo).IsAssignableFrom(typeof(T)))
                    mi = typeof(_FooDispatcher<T>).GetMethod("doFooWithIOkayFoo", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
                else
                    mi = typeof(_FooDispatcher<T>).GetMethod("doFooSomehow", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
                Foo = (Action<T>)(@Delegate.CreateDelegate(typeof(Action<T>), mi.MakeGenericMethod(typeof(T))));
                Foo(param);
            }
        }
like image 35
supercat Avatar answered Sep 27 '22 21:09

supercat