使用C#动态类型的继承类型中的方法调用

3
我在尝试扩展一个基类时注意到了一个意外的行为。以下是这个问题的一个示例:

我有一些代码:

public class Program
{
    static void Main(string[] args)
    {
        var test = new CoolProgram();
        test.Start();
    }

    private void Start()
    {
        var arr = new object[]
        {
            1, // int
            1L, // long
            "Hello World" // string
        };

        foreach (var dyn in arr.Cast<dynamic>())
        {
            DoSomething(dyn);
        }

        Console.ReadKey();
    }

    protected virtual void DoSomething(int i)
    {
        Console.WriteLine("Int:" + i);
    }

    protected virtual void DoSomething(string str)
    {
        Console.WriteLine("Str:" + str);
    }
}
Program 定义了两个方法 DoSomething,分别使用 intstring 参数进行重载。方法 Start 创建了一个对象数组,其中包含装箱的值。定义后,元素将使用强制转换的 `dynamic` 进行迭代。目前为止这样做是可以正常工作的(不包括长整型)。
如果我创建另一个继承类 CoolProgram 并为类型添加了一个针对 long 的新方法,则程序将抛出 RuntimeBinderException 异常,并告诉我最佳匹配已经是 DoSomething(int)。方法 CoolProgram 不会被执行。
public class CoolProgram : Program
{
    protected override void DoSomething(int i)
    {
        // This works
        Console.WriteLine("Cool Int: " + i);
    }

    protected override void DoSomething(string str)
    {
        // This works
        Console.WriteLine("Cool Str: " + str);
    }

    protected virtual void DoSomething(long i)
    {
        // This is a new method for long
        Console.WriteLine("Long:" + i);
    }
}

有人可以解释这种行为或者有解决方案吗?


1
dynamic 只是将类型解析和后续编译步骤推迟到运行时。因此,你真正想知道的是为什么重载解析会执行它正在执行的操作。 - Peter Duniho
2个回答

2
您的示例可以进一步简化:
static void Main(string[] args)
{
    Program test = new CoolProgram();
    dynamic i = 1L;

    test.DoSomething(i);
    //test.Start();
}

问题在于对于Start()方法来说,this的类型是Program。为DoSomething()添加virtual重载在Start()方法的上下文中毫无意义。

同样地,在我的简化示例中,test的编译时类型是Program,就像Start()中的this类型一样。因此,在该上下文中,DoSomething(long)重载不可见,也不能被调用。

如果您想要它可见,需要将其添加到基类中,或确保用于调用DoSomething()的引用的静态类型为CoolProgram。请注意,由于该方法在CoolProgram中声明为protected,因此如果要改变引用的静态类型为CoolProgram,还必须更改可访问性。

最后,如果您真的希望在这里进行完全动态类型解析,可以:

private void Start()
{
    dynamic this2 = this;

    var arr = new object[]
    {
    1, // int
    1L, // long
    "Hello World" // string
    };

    foreach (var dyn in arr.Cast<dynamic>())
    {
        this2.DoSomething(dyn);
    }

    Console.ReadKey();
}

当然,如我上面所指出的那样,这需要将 DoSomething(long) 更改为 public

我纠正了拼写错误。谢谢!现在我明白这里发生了什么。 - dennis

0

1) 第一个问题是访问修饰符protected,因此您无法访问具有Long类型参数的重载方法。我现在将其更改为internal,现在可以访问它了。

2) 第二个问题是您正在创建子类CoolProgram对象,但是您正在调用父类DoSomething方法,您应该使用test.DoSomething(dyn);并使子类对象全局以便在Start方法中访问它。

public class Program
{
static CoolProgram test;
static void Main(string[] args)
{
    test = new CoolProgram();
    test.Start();
}

private void Start()
{
    var arr = new object[]
    {
        1, // int
        1L, // long
        "Hello World" // string
    };
    //test.DoSomething(21474836470);
    foreach (var dyn in arr.Cast<dynamic>())
    {
        test.DoSomething(dyn);
    }

    Console.ReadKey();
}

protected virtual void DoSomething(int i)
{
    Console.WriteLine("Int:" + i);
}

protected virtual void DoSomething(string str)
{
    Console.WriteLine("Str:" + str);
}
}
// from here child class
public class CoolProgram : Program
{
  protected override void DoSomething(int i)
  {
    // This works
    Console.WriteLine("Cool Int: " + i);
    base.DoSomething(i);
 }

 protected override void DoSomething(string str)
 {
    // This works
    Console.WriteLine("Cool Str: " + str);
 }

 internal virtual void DoSomething(long i)
 {
    // This is a new method for long
    Console.WriteLine("Long Int:" + i);
 }

 }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接