通用约束,其中T:struct和T:class

Pie*_*aud 50 c# generics struct constraints class

我想区分以下情况:

  1. 普通值类型(例如int)
  2. 可以为null的值类型(例如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
Run Code Online (Sandbox Code Playgroud)

但是,如果我尝试像这样检测case(3),它就不会编译:

static void Foo<T>(T a) where T : class { } // 3
Run Code Online (Sandbox Code Playgroud)

错误消息是类型'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 ...
Run Code Online (Sandbox Code Playgroud)

我如何Foo(z)编译,将其映射到上述函数之一(或第三个具有另一个约束,我没有想到)?

Alc*_*aro 51

约束不是签名的一部分,但参数是.并且在重载决策期间强制执行参数约束.

所以我们把约束放在一个参数中.这很难看,但它确实有效.

class RequireStruct<T> where T : struct { }
class RequireClass<T> where T : class { }

static void Foo<T>(T a, RequireStruct<T> ignore = null) where T : struct { } // 1
static void Foo<T>(T? a) where T : struct { } // 2
static void Foo<T>(T a, RequireClass<T> ignore = null) where T : class { } // 3
Run Code Online (Sandbox Code Playgroud)

(迟到六年比从未好过?)

  • 哈,好主意!实际上,您不需要将`ignore`参数添加到第二个`Foo <T>`函数中,并使用`T?`. (2认同)

ang*_*son 21

不幸的是,您无法仅根据约束来区分要调用的方法类型.

因此,您需要在不同的类中定义方法或使用不同的名称来定义方法.

  • +1.当然,第一次和第二次的工作因为`T`和`T?`是不同的论点.(`T`和`Nullable <T>`) (2认同)
  • 实际上,如果不介意有一个泛型引用类型的虚拟“可选”参数(该参数对其泛型参数有约束)并且具有“空”默认值,则可以根据约束区分方法调用的类型对于该参数。由于编译器将排除任何无法构造类型的重载,因此虚拟参数类型的约束将有效限制考虑哪些重载。当然,如果编译器可以在没有给出虚拟参数值的调用站点上执行此操作,那么它...... (2认同)

Luk*_*keH 10

除了你对Marnix答案的评论之外,你可以通过一些反思来达到你想要的效果.

在下面的示例中,无约束Foo<T>方法使用反射来调用对相应约束方法的调用 - FooWithStruct<T>或者FooWithClass<T>.出于性能原因,我们将创建并缓存强类型委托,而不是每次Foo<T>调用方法时使用纯反射.

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>();
}
Run Code Online (Sandbox Code Playgroud)

(请注意,此示例不是线程安全的.如果您需要线程安全,那么您需要使用某种锁定围绕对缓存字典的所有访问,或者 - 如果您能够以.NET4为目标 - 使用ConcurrentDictionary<K,V>代替.)


Mar*_*len 5

删除第一个方法上的结构约束.如果需要区分值类型和类,可以使用参数类型来实现.

      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
         }
      }
Run Code Online (Sandbox Code Playgroud)

  • @Maxim:谢谢.我面临的问题是,在非可空方法中,我必须能够调用其他函数来获取并返回`T?`,如果没有`where T:struct`约束,这是无效的. (2认同)