为什么泛型+接口+阴影方法无法一起工作?

时间:2014-07-21 13:48:14

标签: c# generics interface shadow

在C#中,混合泛型,接口和阴影方法(C#中的新关键字)似乎没有像预期的那样起作用(在我看来)。 标记为新(阴影)的显式方法表现为重写方法!

以下是一个用例:

创建一个C#winforms项目,删除生成的Form1.cs 并用以下代码替换Program.cs:

using System;
using System.Text;
using System.Windows.Forms;

namespace GenericWithShadowedMethImplInterface
{

    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.Run(new ChildForm());
        }
    }

    public interface IStepByStep_UI_Initialization
    {
        void FillControls();
        void DefineBindings();
        // etc...
    }

    public class BaseForm : Form, IStepByStep_UI_Initialization
    {
        public StringBuilder Log = new StringBuilder();

        public BaseForm()
        {
            this.InstallSmartLoading();
            Shown += (sender, args) => MessageBox.Show(Log.ToString());
        }

        public void FillControls()
        {
            Log.AppendLine("BaseObject.FillControls");
        }

        public void DefineBindings()
        {
            Log.AppendLine("BaseObject.DefineBinding");
        }
    }

    public class ChildForm : BaseForm, IStepByStep_UI_Initialization
    {
        public ChildForm()
        {
            this.InstallSmartLoading();
        }
        // Shadowing is really what i want
        public new void FillControls()
        {
            Log.AppendLine("ChildObject.FillControls");
        }

        // Shadowing is really what i want
        public new void DefineBindings()
        {
            Log.AppendLine("ChildObject.DefineBinding");
        }
    }

    public static class StepByStepInitializer
    {
        public static void InstallSmartLoading<TForm>(this TForm form)
            where TForm : Form, IStepByStep_UI_Initialization
        {
            // i Use lambda to keep knowing what form type really is (BaseForm or ChildForm)
            form.Load += (_, __) =>
            {
                // I would expect the these two lines of code here...
                // Why these calls are treated as polymorphic calls ?
                form.FillControls(); // always call ChildForm.FillControls even if typeof(TForm) == typeof(BaseForm)
                form.DefineBindings();

                // ... behaves likes this (not generic) code :
                //if (typeof(TForm) == typeof(BaseForm))
                //{
                //    (form as BaseForm).FillControls();
                //    (form as BaseForm).DefineBindings();
                //}
                //else if (typeof(TForm) == typeof(ChildForm))
                //{
                //    (form as ChildForm).FillControls();
                //    (form as ChildForm).DefineBindings();
                //}
            };
        }

    }
}

运行它......

您应该看到:


    ChildObject.FillControls
    ChildObject.DefineBindings
    ChildObject.FillControls
    ChildObject.DefineBindings

现在,如果您在InstallSmartLoading中注释两行代码并取消注释其他代码行,那么再次运行该项目您会看到:


    BaseObject.FillControls
    BaseObject.DefineBindings
    ChildObject.FillControls
    ChildObject.DefineBindings

所以我的问题很简单:为什么你评论过的两行代码的行为与你取消注释的代码不一样?我需要在初始化ChildForm之前完全初始化我的基本BaseForm。这是泛型的限制吗? :((( 有解决方法

  • TForm意味着真正的Form类型,但是......
  • 方法未标记为虚拟/覆盖,我明确使用新关键字
  • 编译器不应该显示警告吗?

提前致谢...

1 个答案:

答案 0 :(得分:1)

你有约束

where TForm : IStepByStep_UI_Initialization

(约束的另一部分现在不相关),那么你有一个变量(实际参数)

TForm form

你做的事情:

form.FillControls(); // always call ChildForm.FillControls even if typeof(TForm) == typeof(BaseForm)

(你提出的问题)。

编译的原因是因为你有这个接口约束。

请注意ChildForm 重新实施 IStepByStep_UI_Initialization已实施的界面BaseForm

所以你的电话真的等同于

((IStepByStep_UI_Initialization)form).FillControls();

TForm无关。重要的是实例如何实现接口。

尝试以下方法:

  • 停止重新实施界面,因此只能在BaseForm
  • 上实施
  • 将接口约束更改为基类约束where TForm : BaseForm

以加强您的理解。


这是一个相关的例子,没有泛型,没有委托和事件,没有Win Forms:

interface ICanTalk
{
  void Talk();
}

class Animal : ICanTalk
{
  public void Talk()
  {
    Console.WriteLine("I am animal");
  }
}
class Dog : Animal, ICanTalk // note: re-implements
{
  public new void Talk() // note: method hiding "new" is always evil
  {
    Console.WriteLine("Wroof!");
  }
}

static class Test
{
  internal static void Run()
  {
    object x = new Dog();

    ((Animal)x).Talk();   // I am animal
    ((Dog)x).Talk();      // Wroof!
    ((ICanTalk)x).Talk(); // Wroof!
  }
}

请注意,将object x = ...上方更改为Animal xDog x是不相关的,这意味着它不会更改所谓的方法。