Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/275.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用泛型将其声明为按钮,但将其视为类的内部控件。为什么?_C#_Generics_.net 2.0_Overriding - Fatal编程技术网

C# 使用泛型将其声明为按钮,但将其视为类的内部控件。为什么?

C# 使用泛型将其声明为按钮,但将其视为类的内部控件。为什么?,c#,generics,.net-2.0,overriding,C#,Generics,.net 2.0,Overriding,这是一个多么棒的网站啊,我已经在这里潜伏了好长时间阅读别人的问题,但现在我有了自己的一个 我的同事写了一个很像下面的课程。我一看到它就知道它不起作用,但我无法向他解释为什么它不起作用 当他将其声明为ControlItem时,他所期望的是在使用base调用Draw()时调用Draw(Button)方法。相反,我们最终总是抛出异常 这是一个协方差问题吗 public abstract class ControlItem { public ControlItem() { }

这是一个多么棒的网站啊,我已经在这里潜伏了好长时间阅读别人的问题,但现在我有了自己的一个

我的同事写了一个很像下面的课程。我一看到它就知道它不起作用,但我无法向他解释为什么它不起作用

当他将其声明为
ControlItem
时,他所期望的是在使用base调用Draw()时调用Draw(Button)方法。相反,我们最终总是抛出异常

这是一个协方差问题吗

public abstract class ControlItem
{
    public ControlItem()
    {
    }

    abstract public void Draw();
}

public class ControlItem<T> : ControlItem where T : Control, new()
{
    public T MyControl { get; set; }

    private ControlItem()
    {       }

    public ControlItem(T control)
        : base()
    {
        MyControl = control;
    }

    public override void Draw()
    {
        Draw(this.MyControl);
    }

    public void Draw(Control cntrl)
    {
        throw new NotImplementedException();
    }

    public void Draw(Button button)
    {
        //Do some work
    }
}
公共抽象类ControlItem
{
公共控制项()
{
}
抽象公共空间图();
}
公共类ControlItem:ControlItem,其中T:Control,new()
{
公共T MyControl{get;set;}
私有控制项()
{       }
公共控制项(T控制)
:base()
{
MyControl=控制;
}
公共覆盖无效绘图()
{
绘制(this.MyControl);
}
公共无效提款(控制中心)
{
抛出新的NotImplementedException();
}
公共作废绘图(按钮)
{
//做些工作
}
}

这是因为编译器只能确定类型将是控件,因此它将始终绑定到带有控件参数的方法。如果需要以不同方式处理它们,则需要在Draw()方法中添加显式检查:

public override void Draw() {
   Button btn = MyControl as Button;
   if (btn != null) {
      Draw(btn);
   } else {
      Draw(this.MyControl);
   }
}
请注意,这不是很“通用”。。。但在你的特殊情况下,它可能会起作用

这是一个协方差问题吗

public abstract class ControlItem
{
    public ControlItem()
    {
    }

    abstract public void Draw();
}

public class ControlItem<T> : ControlItem where T : Control, new()
{
    public T MyControl { get; set; }

    private ControlItem()
    {       }

    public ControlItem(T control)
        : base()
    {
        MyControl = control;
    }

    public override void Draw()
    {
        Draw(this.MyControl);
    }

    public void Draw(Control cntrl)
    {
        throw new NotImplementedException();
    }

    public void Draw(Button button)
    {
        //Do some work
    }
}
不,这是一个静态与动态调度问题。静态分派意味着重载方法调用在编译时根据传入的变量类型绑定到适当的类型:

class Base { }
class Derived : Base { }

class Foo
{
    void Test()
    {
        Base a = new Base();
        Overload(a);    // prints "base"

        Derived b = new Derived();
        Overload(b);    // prints "derived"

        // dispatched based on c's declared type!
        Base c = new Derived();
        Overload(c);    // prints "base"
    }

    void Overload(Base obj)    { Console.WriteLine("base"); }
    void Overload(Derived obj) { Console.WriteLine("derived"); }
}
动态分派意味着函数在运行时根据存储在变量中的对象的实际类型进行绑定:

class Base
{
    public virtual void Override() { Console.WriteLine("base"); }
}

class Derived : Base
{
    public override void Override() { Console.WriteLine("derived"); }
}

class Foo
{
    void Test()
    {
        Base a = new Base();
        a.Override();   // prints "base"

        Derived b = new Derived();
        b.Override();    // prints "derived"

        // dynamically dispatched based type of object stored in c!
        Base c = new Derived();
        c.Override();    // prints "derived"
    }

    void Overload(Base obj) { Console.WriteLine("base"); }
    void Overload(Derived obj) { Console.WriteLine("derived"); }
}
最后一页显示了两者之间的差异。与大多数基于类的OOP语言一样,C#只支持对
this
隐式参数(称为“单一分派”)进行动态分派。换句话说,重写的方法是动态分派的,而重载的方法则不是


<> P>在单调度语言中伪造多个分派的典型解决方案是使用这一方法,这将对你有用。

< P>建立在慷慨的答案上:与C++模板不同,C语言编译器生成的代码对于一个泛型类型来说完全不可知。用于替换满足约束的类型参数的代码的集合。直到运行时,当完全指定的泛型类型的实例被实例化时,JIT编译器才会创建特定于您的类型参数的代码


由于生成的代码适用于满足约束条件的任何对象,因此C编译器将您的
MyControl
成员视为
Control
类型的变量(而不是
T
)因为这是它从约束中可以推断出的。因为编译器必须为类发出泛型代码,它必须根据它所知道的选择调用哪个方法,并且因为它不能确定
MyControl
在运行时是否是
按钮
,它必须选择
Draw(Control)

好的,我明白了你的观点,答案很快就出来了。但是,按照你的观点来看,它和你的例子不一样。在你的例子中,c在编译时被声明为基,但在我的例子中,T在编译时被声明为Button,所以它被声明为Button MyButton。和你的一样,是派生的b。这是真的,但在编译时,它所知道的一切约束是:T是一个控件,所以它绑定到一个控件。这与C++编译之前的模板是不同的。我不知道为什么,但是现在这对我来说是完全有意义的。当然它在编译时会被判定为超载使用。如果它在类库中,它就不知道CL是如何使用的。ass稍后在其他一些用法中实现。谢谢大家的帮助。