Перегрузка параметров универсального типа запрещена? - PullRequest
5 голосов
/ 08 марта 2012

Это отчасти любопытство и отчасти потому, что я просто пытался использовать это. Если у вас есть следующие определения, это не разрешено компилятором, потому что он говорит, что член уже определен. В чем причина запрета исключительных перегрузок параметров универсального типа?

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

Мне кажется, что в этом нет внутренней проблемы. Кто-то может возразить, что не всегда понятно, какой компилятор должен выбирать в случаях, когда определения перекрываются (но общее разрешение в первую очередь кажется наиболее конкретным).

Может ли кто-нибудь помочь мне понять или указать ресурсу, что стоит за запретом этого?

Ответы [ 2 ]

9 голосов
/ 08 марта 2012

Эрик Липперт уже ответил на это в своем блоге об общих ограничениях и сигнатурах методов: http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx

Ограничения на универсальные типы не являются частью сигнатур методов в CLR, поэтому у вас не может быть двух методов, которые отличаются только ограничениями универсальных типов. Без поддержки CLR было бы довольно сложно заставить C # поддерживать их разумным способом, совместимым с другими языками .NET.

1 голос
/ 08 марта 2012

Ограничение struct на Nullable<T> ИМХО действительно неудачно. Что-то вроде Nullable<String> или Nullable<Nullable<Nullable<int>>> может быть расточительным, но что с того? Бокс первый как его содержание; распакуйте его как его содержимое и установите HasValue, если содержимое не равно нулю. Пометьте первый как int, если во всех обнуляемых отчетах HasValue, а при распаковке установите HasValue всех вложенных элементов, если содержимое было ненулевым.

В противном случае я бы предложил создать статический универсальный класс с параметром типа T, который содержит свойство делегата, принимающее T в качестве параметра. Свойство должно возвращать содержимое частного поля, которое должно быть инициализировано, чтобы указывать на метод, который будет проверять тип T и устанавливать для делегата либо версию struct, либо class, в зависимости от ситуации.

Вот пример того, о чем я говорю; в этом используются различные ограничения интерфейса, а не ограничения структуры / класса, но те же принципы могут использоваться так же эффективно.

        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);
            }
        }
...