为什么泛型+接口+影子方法不能一起工作

本文关键字:不能 一起 子方法 工作 影子 泛型 接口 为什么 | 更新日期: 2023-09-27 18:13:58

在c#中,混合使用泛型、接口和影子方法(c#中的新关键字)似乎不会像预期的那样工作(在我看来)。标记为new (shadow)的显式方法表现为重写方法!

下面是一个用例:

创建一个c# winforms项目,删除生成的form .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。FillControlsChildObject。DefineBindingsChildObject。FillControlsChildObject。DefineBindings之前

现在,如果你注释了InstallSmartLoading中的两行代码,并取消注释其他行代码,然后再次运行项目,你应该看到:

<>之前BaseObject。FillControlsBaseObject。DefineBindingsChildObject。FillControlsChildObject。DefineBindings之前

所以我的问题很简单:为什么你注释的两行代码的行为不像你没有注释的代码?我需要我的BaseForm在初始化ChildForm之前被完全初始化。这是泛型的限制吗?:(((是否有解决方法

  • TForm是指真正的Form类型。
  • 方法没有标记为virtual/override,我显式地使用new关键字
  • 编译器不应该显示关于这个的警告吗?

为什么泛型+接口+影子方法不能一起工作

您有约束

where TForm : IStepByStep_UI_Initialization

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

TForm form

你做什么:

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

(你对问题的评论)。

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

注意ChildForm 重新实现了BaseForm已经实现的接口IStepByStep_UI_Initialization

所以你的调用实际上相当于

((IStepByStep_UI_Initialization)form).FillControls();

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

尝试以下操作:

  • 停止重新实现接口,所以只在BaseForm
  • 实现它
  • 将接口约束改为基类约束where TForm : BaseForm

来加强你的理解。


下面是一个相关的例子,没有泛型,没有委托和事件,没有Win窗体:

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是不相关的,这意味着它不会改变调用的方法。