【问题标题】:Generic contraints on method overloads方法重载的一般约束
【发布时间】:2011-12-07 13:44:14
【问题描述】:

我有一个带有一些通用方法的接口,我想实现一个带有重载的方法,以接受一个类的实例,或者它的 PK 值(它可以是一个 int 或 GUID,但会有所不同)。

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

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

其中第二个的“DoSomething”方法名称被突出显示,错误是

类型“ISomeStuff”已经定义了一个名为“DoSomething”的成员 相同的参数类型。

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

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

【问题讨论】:

  • Generic constraints, where T : struct and where T : class 的可能重复项。另请参阅 Eric Lippert 的文章 here
  • @Frederic:我怎么会错过那个!!!
  • 显然侧边栏中的“相关”窗格也没有选择它,所以它可能比平时更棘手;)
  • 嗯,没错 - 我确实扫描了建议列表。

标签: c# generics overloading


【解决方案1】:

有可能,你需要用 C++ 创建类似enable_if 的东西

public class ClassTag<V> where V : class { }

public class StructTag<V> where V : struct { }

public void Func<V>(V v, ClassTag<V> dummy = null) where V : class
{
    Console.Writeln("class");
}

public void Func<V>(V v, StructTag<V> dummy = null) where V : struct
{
    Console.Writeln("struct");
}

public void Func<V>(V? v, StructTag<V> 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 t) // where T : class
{
    Func(t); //error there
}

public static void Z2<T>(T t) where T : class
{
    Func(t); //ok 
}

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

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

唯一的缺点是运行时间成本类似于调用Dictionary&lt;,&gt; 索引。

【讨论】:

  • 我喜欢这个答案。
【解决方案2】:

Jon Skeet 对一切都有答案:click me

引用:

声明仅在通用约束和约束方面有所不同 不是签名的一部分

【讨论】:

【解决方案3】:

如果希望调用一个成员,不管它是否具有类约束或结构约束,并让它调用具有合适约束的方法,则可以定义一个接口IThingUser&lt;T&gt; 以作用于任何类型@987654322 @,以及一个为值类型实现它的类和另一个为类类型实现它的类。有一个静态类ThingUsers&lt;T&gt; 和一个TheUser 类型为IThingUser&lt;T&gt; 的静态字段,并让它用上述类之一的实例填充该字段,然后ThingUsers&lt;T&gt;.theUser 将能够对任何类型的T.

public static class GenTest93
{
    public interface IThingUser<T> { void ActOnThing(T it); }
    class StructUser<T> : IThingUser<T>, IThingUser<Nullable<T>> where T : struct
    {
        void IThingUser<T>.ActOnThing(T it) { System.Diagnostics.Debug.Print("Struct {0}", typeof(T)); }
        void IThingUser<Nullable<T>>.ActOnThing(T? it) { System.Diagnostics.Debug.Print("Struct? {0}", typeof(T)); }
    }
    class ClassUser<T> : IThingUser<T> where T : class
    {
        void IThingUser<T>.ActOnThing(T it) { System.Diagnostics.Debug.Print("Class {0}", typeof(T)); }
    }
    static class ThingUsers<T>
    {
        class DefaultUser : IThingUser<T>
        {
            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<T>)Activator.CreateInstance(t);
                TheUser.ActOnThing(it);
            }
        }
        static IThingUser<T> TheUser = new DefaultUser();
        public static void ActOnThing(T it) {TheUser.ActOnThing(it);}
    }
    public static void ActOnThing<T>(T it) { ThingUsers<T>.ActOnThing(it); }
    public static void Test()
    {
        int? foo = 3;
        ActOnThing(foo);
        ActOnThing(5);
        ActOnThing("George");
    }
}

如果编译器不知道T 满足必要的约束,则必须使用反射来创建StructUser&lt;T&gt;ClassUser&lt;T&gt; 的实例,但这并不难。在第一次ActOnThing&lt;T&gt;() 用于特定的T 之后,ThingUsers&lt;T&gt;.TheUser will be set to an instance which can be used directly for any future calls toActOnThing(),所以性能应该很好。

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

【讨论】:

    【解决方案4】:

    如果你不需要泛型参数,只是想在编译时区分这些情况,你可以使用下面的代码。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-02-19
      • 1970-01-01
      • 2020-06-12
      • 1970-01-01
      • 1970-01-01
      • 2019-12-27
      • 1970-01-01
      相关资源
      最近更新 更多