方法重载的通用约束

我有一个带有一些generics方法的接口,我想实现一个带有重载的方法来接受一个类的实例,或者它的PK值(它是一个int或GUID但确实变化)。

我添加了类似这些示例的方法:

void DoSomething(TKey key) where TKey: struct; void DoSomething(TModel model) where TModel : class; 

其中第二个上的’DoSomething’方法名称突出显示,错误是

类型’ISomeStuff’已经定义了一个名为’DoSomething’的成员,它具有相同的参数类型。

我对此感到惊讶,因为我已经通过参数明确定义了不同的类型:一个是类,另一个是结构。

为什么这不足以使签名不同?

Jon Skeet对所有事情都有答案: 点击我

引用:

声明仅在通用约束中有所不同,约束不是签名的一部分

可以这样做,你需要从C ++创建类似enable_if东西

 public class ClassTag where V : class { } public class StructTag where V : struct { } public void Func(V v, ClassTag dummy = null) where V : class { Console.Writeln("class"); } public void Func(V v, StructTag dummy = null) where V : struct { Console.Writeln("struct"); } public void Func(V? v, StructTag dummy = null) where V : struct { Console.Writeln("struct?"); } static void Main() { Func("A"); Func(5); Func((int?)5); } 

它可以扩展为使用任何不相交的where来区分重载。 唯一的缺点是它不能在另一个通用方法中使用:

 public static void Z1(T t) // where T : class { Func(t); //error there } public static void Z2(T t) where T : class { Func(t); //ok } 

编辑但是在这种情况下有可能使用dynamic来解决这个限制:

 public static void Z1(T t) { Func((dynamic)t); //if `T == int` it will call "struct" version } 

唯一的缺点是运行时成本类似于调用Dictionary<,> index。

如果想要一般地调用成员而不管它是否具有类约束或结构约束,并且让它调用具有合适约束的方法,则可以定义接口IThingUser以对任何类型T ,以及一个是为值类型实现它的类,另一个是为类类型实现它的类。 有一个静态类ThingUsers ,其静态字段为IThingUser类型的TheUser ,并让它用上述类之一的实例填充该字段,然后ThingUsers.theUser将能够对其进行操作任何类型的T

 public static class GenTest93 { public interface IThingUser { void ActOnThing(T it); } class StructUser : IThingUser, IThingUser> where T : struct { void IThingUser.ActOnThing(T it) { System.Diagnostics.Debug.Print("Struct {0}", typeof(T)); } void IThingUser>.ActOnThing(T? it) { System.Diagnostics.Debug.Print("Struct? {0}", typeof(T)); } } class ClassUser : IThingUser where T : class { void IThingUser.ActOnThing(T it) { System.Diagnostics.Debug.Print("Class {0}", typeof(T)); } } static class ThingUsers { class DefaultUser : IThingUser { public void ActOnThing(T it) { Type t = typeof(T); if (t.IsClass) t = typeof(ClassUser<>).MakeGenericType(typeof(T)); else { if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)) t = t.GetGenericArguments()[0]; t = typeof(StructUser<>).MakeGenericType(t); } TheUser = (IThingUser)Activator.CreateInstance(t); TheUser.ActOnThing(it); } } static IThingUser TheUser = new DefaultUser(); public static void ActOnThing(T it) {TheUser.ActOnThing(it);} } public static void ActOnThing(T it) { ThingUsers.ActOnThing(it); } public static void Test() { int? foo = 3; ActOnThing(foo); ActOnThing(5); ActOnThing("George"); } } 

如果编译器不知道T满足必要的约束,则必须使用Reflection来创建StructUserClassUser的实例,但这并不太难。 在第一次使用ActOnThing() ,特定的TThingUsers.TheUser will be set to an instance which can be used directly for any future calls to ActOnThing()的ThingUsers.TheUser will be set to an instance which can be used directly for any future calls to ,因此性能应该非常好。

请注意,如果给定Nullable ,则该方法创建一个StructUser并将其强制转换为IThingUser> ,而不是尝试创建sometype> ,因为可空类型本身不会满足任何约束。

如果您不需要通用参数,并且只想在编译时区分这些情况,则可以使用以下代码。

 void Foo(object a) { } // reference type void Foo(T? a) where T : struct { } // nullable void Foo(ValueType a) { } // value type