Универсальные ограничения, где T: структура и где T: класс

Я хотел бы дифференцироваться между следующими случаями:

  1. Простой тип значения (например. int)
  2. nullable тип значения (например. int?)
  3. Ссылочный тип (например. string) - дополнительно, я не заботился бы, отобразилось ли это на (1) или (2) выше

Я придумал следующий код, который хорошо работает для случаев (1) и (2):

static void Foo<T>(T a) where T : struct { } // 1

static void Foo<T>(T? a) where T : struct { } // 2

Однако, если я пытаюсь обнаружить случай (3) как это, он не компилирует:

static void Foo<T>(T a) where T : class { } // 3

Сообщением об ошибке является Тип 'X', уже определяет участника по имени 'Foo' с теми же типами параметра. Ну, так или иначе я не могу иметь значения между where T : struct и where T : class.

Если я удаляю третью функцию (3), следующий код не компилирует также:

int x = 1;
int? y = 2;
string z = "a";

Foo (x); // OK, calls (1)
Foo (y); // OK, calls (2)
Foo (z); // error: the type 'string' must be a non-nullable value type ...

Как я могу добраться Foo(z) скомпилировать, отображая его на одну из вышеупомянутых функций (или третью с другим ограничением, о котором я не думал)?

50
задан Pierre Arnaud 4 June 2010 в 02:23
поделиться

3 ответа

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

В приведенном ниже примере неограниченный метод Foo использует отражение для передачи вызовов соответствующего ограниченного метода - либо FooWithStruct , либо FooWithClass < T> . По соображениям производительности мы создадим и кэшируем строго типизированный делегат, а не будем использовать простое отражение при каждом вызове метода Foo .

int x = 42;
MyClass.Foo(x);    // displays "Non-Nullable Struct"

int? y = 123;
MyClass.Foo(y);    // displays "Nullable Struct"

string z = "Test";
MyClass.Foo(z);    // displays "Class"

// ...

public static class MyClass
{
    public static void Foo<T>(T? a) where T : struct
    {
        Console.WriteLine("Nullable Struct");
    }

    public static void Foo<T>(T a)
    {
        Type t = typeof(T);

        Delegate action;
        if (!FooDelegateCache.TryGetValue(t, out action))
        {
            MethodInfo mi = t.IsValueType ? FooWithStructInfo : FooWithClassInfo;
            action = Delegate.CreateDelegate(typeof(Action<T>), mi.MakeGenericMethod(t));
            FooDelegateCache.Add(t, action);
        }
        ((Action<T>)action)(a);
    }

    private static void FooWithStruct<T>(T a) where T : struct
    {
        Console.WriteLine("Non-Nullable Struct");
    }

    private static void FooWithClass<T>(T a) where T : class
    {
        Console.WriteLine("Class");
    }

    private static readonly MethodInfo FooWithStructInfo = typeof(MyClass).GetMethod("FooWithStruct", BindingFlags.NonPublic | BindingFlags.Static);
    private static readonly MethodInfo FooWithClassInfo = typeof(MyClass).GetMethod("FooWithClass", BindingFlags.NonPublic | BindingFlags.Static);
    private static readonly Dictionary<Type, Delegate> FooDelegateCache = new Dictionary<Type, Delegate>();
}

(Обратите внимание, что этот пример не является потокобезопасным . Если вам требуется потокобезопасность, тогда вам нужно будет либо использовать какую-то блокировку для всего доступа к словарю кеша, либо - если вы ' можно настроить таргетинг на .NET4 - вместо этого используйте ConcurrentDictionary .)

10
ответ дан 7 November 2019 в 10:55
поделиться

Отбросьте ограничение структуры для первого метода. Если вам нужно различать типы значений и классы, вы можете использовать для этого тип аргумента.

      static void Foo( T? a ) where T : struct
      {
         // nullable stuff here
      }

      static void Foo( T a )
      {
         if( a is ValueType )
         {
            // ValueType stuff here
         }
         else
         {
            // class stuff
         }
      }
5
ответ дан 7 November 2019 в 10:55
поделиться

К сожалению, вы не можете различить тип вызываемого метода на основе только ограничений.

Поэтому вам нужно вместо этого определить метод в другом классе или с другим именем.

21
ответ дан 7 November 2019 в 10:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: