使用泛型属性调用重载方法会调用错误的重载

jus*_*mer 4 c# vb.net generics overloading

我有一个基本的过滤器类,存储string参数名称和通用T值.过滤器有一个方法Write(writer As IWriter),可以将过滤器的内容写入HTML页面.该Write方法有两个重载,一个带两个字符串,它接受一个字符串和一个对象.这让我自动引用字符串.

问题是,当我调用writer.Write(ParameterName, Value)并且T是a时string,它调用字符串/对象的重载,而不是字符串/字符串!如果我writer直接调用Write方法,它会按预期工作.

这是C#中的SSCE.我在VB和C#中测试了这个,发现了同样的问题

void Main() {
    FooWriter writer = new FooWriter();

    Filter<string> f = new Filter<string>() {ParameterName = "param", Value = "value"};

    f.Write(writer);                        //Outputs wrote w/ object
    writer.Write(f.ParameterName, f.Value); //Outputs wrote w/ string
}

class FooWriter {
    public void Write(string name, object value) {
        Console.WriteLine("wrote w/ object");
    }

    public void Write(string name, string value) {
        Console.WriteLine("wrote w/ string");
    }
}

class Filter<T> {
    public string ParameterName {get; set;}
    public T Value {get; set;}

    public void Write(FooWriter writer) {
        writer.Write(ParameterName, Value);
    }
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 7

问题是,当我调用writer.Write(ParameterName,Value)而T是一个字符串时,它调用字符串/对象的重载,而不是字符串/字符串!

是的,这是预期的 - 或者说,它的行为符合规定.编译器通常根据参数的编译时类型选择重载.有没有从隐式转换Tstring,因此对于调用的唯一适用的候选人writer.Write(ParameterName, Value)Write(string, object)超载.

如果您希望它在执行时执行重载解析,则需要使用动态类型.例如:

public void Write(FooWriter writer) {
    // Force overload resolution at execution-time, with the execution-time type of
    // Value.
    dynamic d = Value;
    writer.Write(ParameterName, d);
}
Run Code Online (Sandbox Code Playgroud)

请注意,这仍然会出乎意料地行为 - 如果Value为null,那么它将等同于调用writer.Write(ParameterName, null)哪个将使用"更好"的函数成员 - 这里writer.Write(string, string)- 即使类型Tobject!

  • @ just.another.programmer:`T`的编译时类型只是`T` ...但是有一个`T`到`object`的隐式转换,但不是从`T`到`string`的转换.`Filter <T>`只编译*一次* - 基本上你需要更新你对泛型如何工作的理解. (2认同)