Я хотел бы дифференцироваться между следующими случаями:
int
)int?
)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)
скомпилировать, отображая его на одну из вышеупомянутых функций (или третью с другим ограничением, о котором я не думал)?
В дополнение к вашему комментарию к ответу Марникса , вы можете добиться того, чего хотите, используя немного рефлексии.
В приведенном ниже примере неограниченный метод 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
.)
Отбросьте ограничение структуры для первого метода. Если вам нужно различать типы значений и классы, вы можете использовать для этого тип аргумента.
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
}
}
К сожалению, вы не можете различить тип вызываемого метода на основе только ограничений.
Поэтому вам нужно вместо этого определить метод в другом классе или с другим именем.