方法重载的通用约束
我有一个带有一些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
将能够对其进行操作任何类型的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来创建StructUser
或ClassUser
的实例,但这并不太难。 在第一次使用ActOnThing
,特定的T
, ThingUsers
ActOnThing()的ThingUsers
,因此性能应该非常好。
请注意,如果给定Nullable
,则该方法创建一个StructUser
并将其强制转换为IThingUser
,而不是尝试创建sometype
,因为可空类型本身不会满足任何约束。
如果您不需要通用参数,并且只想在编译时区分这些情况,则可以使用以下代码。
void Foo(object a) { } // reference type void Foo(T? a) where T : struct { } // nullable void Foo(ValueType a) { } // value type