在重载方法上使用泛型参数

时间:2016-02-11 13:04:23

标签: c# generics methods parameters

为什么此程序会输出Generic Value而不是Hello world!

using System;

class Example
{
    public static void Print<T>(T value)
    {
        Console.WriteLine("Generic Value");
    }

    public static void Print(string value)
    {
        Console.WriteLine(value);
    }

    public static void GenericFunc<T>(T value)
    {
        Print(value);
    }

    static void Main()
    {
        GenericFunc("Hello world!");
    }
}

泛型方法参数如何在C#的引擎下翻译?

2 个答案:

答案 0 :(得分:14)

重载分辨率仅在编译时完成。

由于GenericFunc<T>在编译时不知道Tstring还是其他内容,因此它只能使用Print<T>(T value)“重载”。< / p>

使用dynamic,您可以将其更改为动态调度,并获得您期望的行为:

Print((dynamic)value);

这使得重载解析在运行时发生,实际运行时类型为value

答案 1 :(得分:1)

简单回答简单问题

另一个答案解释了泛型绑定的方式(编译时)。但它并没有回答OOP,良好实践,或者只是为什么你不应该首先编写这些代码。

<强> OOP

OOP中的第一个O表示对象,没有静态方法。

责任

让我们考虑该方法的通用versin作为负责打印一组不同可能类型的方法。 String类型是集合的一部分。因此,它应该由您的Print函数的通用版本管理。

public static void Print<T>(T value)
{
    Console.WriteLine(value.ToString());
}

然后你遇到了ref类型的无效问题。

    public static void Print<T>(T value) where T : class
    {
        if (value != null)
        {
            Console.WriteLine(value.ToString());
        }
    }

    public static void GenericFunc<T>(T value) where T : class
    {
        Print(value);
    }

对于那些知道为什么你不应该使用动态的人,除非在某些情况下(see my anwer on that)。

更干净的OOP解决方案

现在假设您有不同的对象要打印。每个对象都应该负责知道如何显示它。首先,因为它不会泄漏内部数据到外部世界,从而简化了数据的封装。其次,因为你在内部数据和打印功能之间有内在的耦合,所以两者都应该位于同一个地方:在课堂内。这是ToString功能的目的。

让我们走一些......

现在,我们可以想象它不是打印功能,而是其他东西。

我们得到了一个类的层次结构,在同一个函数上有重载(让它称之为Foo)以及这些类的实例集合,你必须为它们调用函数Foo。然后让所有这些类实现IFooCallable接口:

public interface IFooCallable
{
    void Foo();
}

稍微复杂......

好的,但是想象一下,没有通用的方法来处理所有这些类的实例,因为它们有很大不同。

让我们拨打Visitor pattern。当您想要分析某个对象树时,每个节点都非常不同(例如AST),这是常用的。 它是一种已知的模式,可以轻松与您的团队分享知识渊博的信息。

你有访客

public class Visitor : IVisitor
{
    public void Visit(Foo foo)
    {
        // do something with foo
    }

    public void Visit(Bar bar)
    {
        // do something with bar
    }
}

可访问

public class Foo : IVisitable
{
    public void Accept(IVisitor visitor)
    {
        visitor.Visit(this);
    }
}

此外,这种模式是可重用的(如果需要,可以编写几种IVisitor实现)。

我不买dynamic的东西。特别是当有更清洁,更快的替代品时。如果动态如此之大,为什么不写这个;)

    public static void Print(dynamic value)
    {
        Console.WriteLine(value);            
    }

    public static void GenericFunc(dynamic value)
    {
        Print(value);
    }

    static void Main(dynamic[] args)
    {
        GenericFunc((dynamic)"Hello World");
    }