重载方法..为什么基类具有优先权?

时间:2012-10-26 23:01:37

标签: c# c#-4.0

在下面的代码中,我有一个重载方法,一个采用ClazzA类型的参数,另一个采用ClazzB类型的参数。在显示的代码中,调用第一个GetDescription方法(以ClazzA作为参数的方法)。我想我理解为什么。
我的问题是..如果底层对象的类型为classB(不必检查每个对象并将其转换为clazzB),那么有一种优雅的方法可以使clazzB首先调用它吗?

public class ClazzA
{
    public virtual string Descr { get { return "A"; } }
}

public class ClazzB : ClazzA
{
    public override string Descr { get { return "B"; } }
}

public static class test
{
    public static void Main()
    {
        ClazzA test = new ClazzB();
        GetDecription(test);
    }

    public static void GetDecription(ClazzA someClazz)
    {
        Debug.WriteLine("I am here");
    }

    public static void GetDecription(ClazzB someClazz)
    {
        Debug.WriteLine("I want to be here");
    }
}

输出:“我在这里”

我真的希望调用第二种方法,因为'test'属于ClassB类型。我唯一有两个解决方案是:

  1. if(test is ClazzB)       返回GetDescription((ClazzB)测试);

    1. 在ClassA中做几乎相同的事情......检查类型并委托第二种方法
    2. 这两个都需要检查对象以确定其类型

8 个答案:

答案 0 :(得分:7)

重载是在编译时确定的。引用的编译时类型为ClazzA,以便选择重载。你要求的是与多次派遣有关。 C#和许多其他语言(如C ++和Java)仅支持单一调度(通过virtual方法)。人们已经通过多种方式来解决这个问题。最纯粹的OO方式是访客模式。您修改类以包含方法(Accept),然后将this引用传递给访问者(Visit)上的方法。这是有效的,因为您覆盖每个子类中的Accept方法,以便this将成为对象的实际类型。所有访问者需求都是您要支持的每个子类的特定方法(有关详细信息,请参阅wikipedia)。

样本:

public class ClazzA
{
   public virtual string Accept(ClassVisitor visitor)
   {
      return visitor.Visit(this);
   }
}

public class ClazzB : ClazzA
{
   public override string Accept(ClassVisitor visitor)
   {
      return visitor.Visit(this);
   }
}

public abstract class ClassVisitor
{
  public abstract string Visit(ClazzA a);
  public abstract string Visit(ClazzB b);
}

public class GetDescriptionVisitor : ClassVisitor
{
    public override string Visit(ClazzA a)
    {
       return "A";
    }

    public override string Visit(ClazzB b)
    {
       return "B";
    }
}

用法:

ClassVisitor visitor = new GetDescriptionVisitor();
ClazzA b = new ClazzB();
Console.WriteLine(b.Accept(visitor)); // prints "B"

答案 1 :(得分:2)

您尝试做的事情可能更好地使用多态,如下所示:

public interface IProvideDescription { 
  string GetDescription();
}

public class A : IProvideDescription {
  public string GetDescription() {
    return "I'm an A";
  }
}

public class B : IProvideDescription {
  public string GetDescription() {
    return "I'm a B";
  }
}

// to execute:

IProvideDescription x = new A();
Console.WriteLine(x.GetDescription());
x = new B();
Console.WriteLine(x.GetDescription());

答案 2 :(得分:2)

因为方法重载解析在编译时发生。在处理这种情况时,如果您使用的是C#4,则可以使用dynamic,以便将重载决策推迟到执行时。

dynamic instance = new ClazzB();
Console.WriteLine(GetDescription(instance));

或者,您可以使用类似以下的访问者模式,但这种双重调度方法感觉就像很多工作。请注意必须在每个派生类型中重新实现的重复Visit方法!

public interface IVisitable
{
    string Visit(DescriptionVisitor visitor);
}

public class ClazzA : IVisitable
{
    public virtual string Visit(DescriptionVisitor visitor)
    {
        return visitor.Visit(this);
    }
}

public class ClazzB : ClazzA
{
    public override string Visit(DescriptionVisitor visitor)
    {
        return visitor.Visit(this);
    }
}

public class DescriptionVisitor
{
    public string Visit(ClazzA item) { return "Description A"; }
    public string Visit(ClazzB item) { return "Description B"; }
}

然后,以下内容最终仍会调用带有ClazzB的DescriptionVisitor中的重载。

var visitor = new DescriptionVisitor();
ClazzA a = new ClazzB();
Console.WriteLine(a.Visit(visitor));

答案 3 :(得分:1)

要回答标题中的问题(“...为什么基类具有优先权?”),请查看您的变量test被声明为什么(回答:您的基类)。选择重载时,所有方法调用都知道您正在将ClazzA类型的变量传递给它。当然,您已为其指定了ClazzB类型的对象,但假设您的赋值语句更复杂:

ClazzA test = GiveMeSomeObject();

方法选择必须在编译时进行,以提供类型安全性。

答案 4 :(得分:1)

通常,识别类类型不是外部类的责任。 如果你需要多态行为,只需将GetDescription作为虚函数放入ClassA,然后在ClassB中重写 - 这在概念上是正确的。

答案 5 :(得分:0)

正如@roken所提到的,您的示例实际上会导致B,因为Descr属性被覆盖。如果这就是您正在做的全部,请删除ClazzB重载并使用您已经获得的多态行为。如果你真的需要在方法中做一些不同的事情并且重载是最好的方法,你可以通过dynamic重载决策来实现:

GetDecription((dynamic)test);

然而,这有一些缺点,例如性能和缺乏GetDescription(test)有意义的编译时测试。我建议在GetDecription(ClazzA)中进行运行时检查:

if (someClazz is ClazzB)
{
    GetDescription((ClazzB)someClazz);
    return;
}

答案 6 :(得分:0)

您可以使用.Net 4.0中引入的“dynamic”关键字来获取所需的行为。它在运行时评估类型,并选择正确的重载。

public static class test
{
    public static void Main()
    {
        dynamic test = new ClazzB();
        GetDecription(test);
    }

    public static void GetDecription(ClazzA someClazz)
    {
        Debug.WriteLine("I am here");
    }

    public static void GetDecription(ClazzB someClazz)
    {
        Debug.WriteLine("I want to be here");
    }
}

答案 7 :(得分:-2)

您只需使用一个GetDescription()方法即可:

public String GetDescription(ClassA in) {
    if (in is ClassB) {
        return (in as ClassB).Descr 
    }

    return in.Descr;
}