简体   繁体   English

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

[英]Using a generic parameter on overloaded methods

Why does this program output Generic Value and not Hello world! 为什么这个程序输出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!");
    }
}

How is the generic method parameter being translated under the hood of C#? 泛型方法参数如何在C#的引擎下翻译?

Overload resolution is only done at compile-time. 重载解析仅在编译时完成。

Since GenericFunc<T> doesn't know whether T is a string or something else at compile-time, it can only ever use the Print<T>(T value) "overload". 由于GenericFunc<T>在编译时不知道Tstring还是其他东西,因此它只能使用Print<T>(T value) “overload”。

Using dynamic , you can change this to a dynamic dispatch, and get the behaviour you expect: 使用dynamic ,您可以将其更改为动态分派,并获得您期望的行为:

Print((dynamic)value);

This makes the overload resolution happen at runtime, with the actual runtime type of value . 这使得重载解析在运行时发生,具有实际的运行时类型value

Simple answer to simple question 简单回答简单的问题

The other answer explains the way generic are bound (compile-time). 另一个答案解释了泛型绑定的方式(编译时)。 But it doesn't answer on the OOP, good practice, or simply why you should never write this code in the first place. 但它没有回答OOP,良好实践,或者只是为什么你不应该首先编写这个代码。

OOP OOP

The first O in OOP means Object and there is none with only static methods. OOP中的第一个O表示对象,没有静态方法。

Responsibility 责任

Let's think about the Generic versin of the method as a method responsible for printing a set of different possible types. 让我们考虑该方法的通用versin作为负责打印一组不同可能类型的方法。 String type is part of the set. String类型是集合的一部分。 So it should be managed by the generic version of your Print function. 所以它应该由Print函数的通用版本管理。

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

then you got the problem of nullity for ref types. 然后你得到了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);
    }

And for those who are aware of why you should not use dynamic unless in some cases ( see my anwer on that ). 对于那些知道为什么你不应该使用动态的人,除非在某些情况下( 参见我的答案 )。

More clean OOP solution 更清洁的OOP解决方案

Now imagine you have different objects to print. 现在假设您有不同的对象要打印。 Each object should be responsible for knowing how to display it. 每个对象都应该负责知道如何显示它。 Firstly, because it ease encapsulation of data by not leaking internal data to the external world. 首先,因为它不会泄漏内部数据到外部世界,从而简化了数据的封装。 Secondly, because you've got an inherent coupling between the internal data and the printing function, so both should be located in the same place: inside the class. 其次,因为内部数据和打印功能之间存在固有的耦合,所以两者都应该位于同一个地方:类内部。 That's the purpose of the ToString function. 这就是ToString函数的目的。

Let's take some height... 我们需要一些高度......

Now, we could imagine that it's not a Print function but something else. 现在,我们可以想象它不是打印功能而是其他功能。

We got a hierarchy of classes with overload on the same function (let's call it Foo ) and a collection of instances from these classes for which you must call the function Foo . 我们得到了一个类的层次结构,在同一个函数上重载(让我们称之为Foo )和这些类的实例集合,你必须为它们调用函数Foo Let's then make all these classes implement the IFooCallable interface : 然后让所有这些类实现IFooCallable接口:

public interface IFooCallable
{
    void Foo();
}

A little more complex... 更复杂......

Ok, but imagine that there is not common way to process all these instances of classes because there are very different. 好的,但是想象一下,没有通用的方法来处理所有这些类的实例,因为它们有很大不同。

Let's call the Visitor pattern . 我们称之为访客模式 It's commonly used when you want to analyze some object tree with each node very different (like in AST ). 当你想要分析一些每个节点非常不同的对象树时(例如在AST中 ),它常用。 It's a known pattern, making it easy to share knowledgable information with your team. 这是一种已知模式,可以轻松与您的团队分享知识渊博的信息。

You got the Visitor : 你有访客

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

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

and the Visitable 的Visitable

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

Moreover this pattern is reusable (you could write several implementation of IVisitor should you need to). 此外,这种模式是可重用的(如果需要,可以编写几种IVisitor实现)。

I don't buy the dynamic thing. 我不买dynamic东西。 Especially when there is more cleaner, faster alternative. 特别是当有更清洁,更快的替代品时。 If dynamic so great, why not writing this then ;) 如果动态如此之大,为什么不写这个;)

    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");
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM