是否有可能让 c# 使用大多数特定类型而不是基类型的方法重载?

Is it possible to get c# to use method overload of most specific type rather than base type?

如果你有一个用派生类型重载的方法,在 运行 时调用的方法取决于你的变量的类型,即使底层对象实际上是派生类型:

class Program
{
    static void Main(string[] args)
    {
        BaseClass theBaseObject = new BaseClass
        {
            Foo = "FOO"
        };
        DerivedClass theDerivedObject = new DerivedClass
        {
            Foo = "FOO",
            Bar = "BAR"
        };

        Processor processor = new Processor();

        Console.WriteLine(processor.Compose(theBaseObject));
        Console.WriteLine(processor.Compose(theDerivedObject));
        Console.WriteLine(processor.Compose((BaseClass) theDerivedObject));
    }
}

public class Processor
{
    public string Compose(BaseClass item)
    {
        return item.Foo;
    }

    public string Compose(DerivedClass item)
    {
        return Compose((BaseClass)item) + item.Bar;
    }
}

public class BaseClass
{
    public string Foo { get; set; }
}

public class DerivedClass : BaseClass
{
    public string Bar { get; set; }
}

实际输出:

FOO
FOOBAR
FOO

我想找到一种方法来改变这种行为,以便为给定参数调用最具体的方法。

期望的输出:

FOO
FOOBAR
FOOBAR  // because theDerivedObject is an instance of DerivedClass 

这将允许对一堆项目进行特定处理,所有项目都来自一个基础。

这在 c# 中可行吗?


编辑:

澄清 - 实际上不会有显式转换,因为这些项目可能在混合类型集合的列表中:

使用没有显式转换的列表的示例:

    foreach (BaseClass item in new []{ theBaseObject, theDerivedObject })
    {
        Console.WriteLine(processor.Compose(item));
    }

实际输出:

FOO
FOO

期望的输出:

FOO
FOOBAR

显然演员仍在发生 - 但要删除它并不容易。

首先,你得到结果的原因是你得到你得到的结果是因为你特别声明了 theDerivedObject as DerivedClass 因此它将 DerivedClass 作为参数并带有 (BaseClass) theDerivedObject) 你基本上只是告诉编译器(如果我对编译器有误,请纠正我)或程序采用带有参数 BaseClass

的方法

您要做的是以下选项之一

1 号:

public class Processor
{
    public string Compose(BaseClass item)
    {
        if (item is DerivedClass)
            return Compose((DerivedClass) item);
        return item.Foo;
    }

    public string Compose(DerivedClass item)
    {
        return item.Foo + item.Bar;
    }
}

2号

public class Processor
{
    public string Compose(BaseClass item)
    {
        if (item is DerivedClass)
        {
            var derived = (DerivedClass) item;
            return derived.Foo + derived.Bar;
        }
        return item.Foo;
    }
}

或者您可能想要覆盖 ToString()

public class Processor
{
    public string Compose(BaseClass item)
    {
        var @class = item as DerivedClass;
        return @class?.ToString() ?? item.ToString();
    }
}

public class BaseClass
{
    public string Foo { get; set; }

    public override string ToString()
    {
        return Foo;
    }
}

public class DerivedClass : BaseClass
{
    public string Bar { get; set; }

    public override string ToString()
    {
        return Foo + Bar;
    }
}

如果您不使用 C#6.0,那么处理器将是以下

public class Processor
{
    public string Compose(BaseClass item)
    {
        var @class = item as DerivedClass;
        if (@class != null)
        {
            return @class.ToString();
        }
        return item.ToString();
    }
}
public class Processor
{
    public string Compose(BaseClass item)
    {
        return item.Compose();
    }
}

public class BaseClass
{
    public string Foo { get; set; }

    public virtual string Compose()
    {
        return Foo;
    }
}

public class DerivedClass : BaseClass
{
    public string Bar { get; set; }

    public override string Compose()
    {
        return base.Compose() + Bar;
    }
}

难怪你得到了你得到的东西。您重载了 Compose 方法。您声明两个签名,一个用于基础 class,另一个用于派生。然后多次调用该方法:

  • processor.Compose(theBaseObject) 呼叫 Compose(BaseClass item)
  • processor.Compose(theDerivedObject) 呼叫 Compose(DerivedClass item)。这里碰巧有几种方法可能适合调用。编译器选择一个在层次结构中具有最接近基数 class 的方法,它本身就是 DerivedClass.
  • processor.Compose((BaseClass) theDerivedObject)) 打电话给 Compose(BaseClass item) 因为你告诉他这样做。它没有可供选择的选项。

您可能对 class virtual 成员 overriding 感兴趣。参见

public class BaseClass
{
    public string Foo { get; set; }
    public virtual string GetComposeResult() { return Foo; }
}

public class DerivedClass : BaseClass
{
    public string Bar { get; set; }
    public override string GetComposeResult() { return Foo + Bar; }
}

在这种情况下,使用以下代码将达到您的预期。

public class Processor
{
    public string Compose(BaseClass item) { return item.GetComposeResult(); }
}

这段代码让我想起了 Haddocks' Eyes 诗:

But I was thinking of a plan
 To dye one's whiskers green,
And always use so large a fan
 That they could not be seen.

首先,您的代码创建了一个子class,但随后它将对象转换回基础class,因此编译器看不到实际类型!您的示例中的重载在编译时已解决,因此将调用 Compose(BaseClass item)

您可以扭转局面,让 .NET 为您动态解析重载,方法是隐藏所有重载,并公开采用基数 class 并执行动态转换的单个方法:

public class Processor {
    public string Compose(BaseClass item) {
        return ComposeImpl((dynamic)item);
    }
    private string ComposeImpl(BaseClass item) {
        return item.Foo;
    }
    private string ComposeImpl(DerivedClass item) {
        return ComposeImpl((BaseClass)item) + item.Bar;
    }
}

"magic"在这一行:

return ComposeImpl((dynamic)item);

item 强制转换为 dynamic,这告诉系统必须根据 运行 的时间类型来选择 ComposeImpl 的实际过载BaseClass 对象。