C#泛型可以这么酷吗?

时间:2014-01-28 18:20:01

标签: c# generics overloading

我希望能够做到这样的事情:

class A<T1, T2>
{
    public void Call(T1 arg1, T2 arg2)
    {
        B b = new B();
        b.DoSomething(arg1); // determine which overload to use based on T1
        b.DoSomething(arg2); // and T2
    }
}   

class B
{
    public void DoSomething(int x)
    {
        // ...
    }

    public void DoSomething(float x)
    {
       // ...
    }
}

我知道可以使用if / else检查来完成,但这似乎并不优雅,特别是当我有20多种类型可供选择时。

4 个答案:

答案 0 :(得分:8)

如果您想要类型安全,您可以做的最好的事情可能是:

class A<T1, T2>
{
    public void Call<T>(T1 arg1, T2 arg2) where T : IDoSomething<T1>, IDoSomething<T2>, new()
    {
        T b = new T();
        b.DoSomething(arg1);
        b.DoSomething(arg2);
    }
}

public interface IDoSomething<T>
{
    void DoSomething(T arg);
}

public class B : IDoSomething<int>, IDoSomething<float>
{
    void IDoSomething<int>.DoSomething(int arg)
    {
        Console.WriteLine("Got int {0}", arg);
    }

    void IDoSomething<float>.DoSomething(float arg)
    {
        Console.WriteLine("Got float {0}", arg);
    }
}
你可以使用

var a = new A<int, float>();
a.Call<B>(1, 4.0f);

答案 1 :(得分:4)

此方法不是类型安全的或可取的

可以将变量声明为dynamic,这意味着它的类型将在运行时解析,而不是编译时。

public void Call(dynamic arg1, dynamic arg2)
{
    B b = new B();
    b.DoSomething(arg1); // determine which overload to use based on T1
    b.DoSomething(arg2); // and T2
} 

请注意,如果arg1原来是一个字符串,并且没有适当的过载,那么您将获得RuntimeBinderException。 此外,由于您推迟了类型解析,因此性能略有下降。

答案 2 :(得分:0)

是的,他们很酷。

但是,它只适用于继承层次结构,因为在您的示例中:arg1必须是int,而arg2必须是float。您将收到编译器错误,因为类型声明T1,T2不限于float和int类型。

如果arg1是BaseA而arg2是BaseB,那么这个例子会更多。

然后你会像这样声明A类:

class A<T1, T2> where T1: BaseA, T2: BaseB

现在,对于任何T1继承自BaseA等的情况,它都可以。

当您使用A类时,运行时将知道实际使用的是什么类型,并选择正确的重载。

答案 3 :(得分:0)

您可以执行此操作,但仅在特定情况下,如果您可以对泛型使用where约束,并且编译器知道会发生什么。

但是,除非您使用dynamic,否则必须知道会发生什么。另请注意,where泛型类型约束必须是类或接口。它不能(例如)int

简单的编译和运行的示例:

static void Main(string[] args)
{
    A<C, D> test = new A<C, D>();
    test.Call(new Cimpl(), new Dimpl());
}

class A<T1, T2> where T1 : C where T2 : D
{
    public void Call(T1 arg1, T2 arg2)
    {
        B b = new B();
        b.DoSomething(arg1); // determine which overload to use based on T1
        b.DoSomething(arg2); // and T2
    }
}

class Cimpl : C { }

class Dimpl : D { }

interface C
{ }

interface D
{ }

class B
{
    public void DoSomething(C x)
    {
        // ...
    }

    public void DoSomething(D x)
    {
        // ...
    }
}