【问题标题】:overriding abstract generic method from non generic class覆盖非泛型类的抽象泛型方法
【发布时间】:2012-03-01 03:04:26
【问题描述】:

基类

class Drawer
{
    public abstract void Draw<T>(T type);    
}

派生类#1

class ADrawer : Drawer
{
    public override void Draw<T>(List<T> list)
    {
        foreach (var a in list)
        {
            DrawA(a);
        }
    }

    public void DrawA(Agent a)
    {
        //draw code here
    }
}

派生类#2

class AnotherDrawer : Drawer
{
    public override void Draw<T>(T number)
    {
        if (number == 1)
        {
            //draw code
        }
    }
}

错误在 #1 派生类中:“找不到合适的方法来覆盖”

我应该在基类中使用“virtual”以及“abstract”吗?

我应该如何设置基参数类型以允许派生类中的各种参数?

【问题讨论】:

  • 顺便说一句,抽象方法隐含地是虚拟方法,因此您不必将方法定义为抽象和虚拟。
  • 您有两种概念上不同的方法。您的“绘制”方法之一绘制一个事物,另一个绘制许多事物。首先,您不应该尝试将它们变成相同的方法;制作两个方法:Draw&lt;T&gt;(T item)DrawMany&lt;T&gt;(IEnumerable&lt;T&gt; items)。与List&lt;T&gt; 相同,有AddAddRange 方法;对一件事情做某事和对很多事情做某事是两种不同的操作,所以有两种不同的方法。

标签: c# generics overriding abstract


【解决方案1】:

抽象方法应该有这个签名

  public abstract void Draw<T>(List<T> type);  

【讨论】:

  • 嗯,好的,但是我有多个来自基类型的派生类,每个派生类都采用不同的参数类型,那么如何“通用”设置基参数?
  • 我建议让基类具有泛型参数,然后派生类可以实现Drawer&lt;int&gt;Drawer&lt;Agent&gt;等。
【解决方案2】:

要让它编译,请将基类更改为:

class Drawer
{
    public abstract void Draw<T>(List<T> type);    
}

List&lt;T&gt;T 不同,因此当您在派生类的方法中传入List&lt;T&gt; 时,您不能覆盖基方法,因为它具有T 参数,而不是@ 987654326@参数。

【讨论】:

    【解决方案3】:

    您的代码存在的问题不仅仅是您询问的问题。暂且不考虑覆盖问题,ADrawer 类需要一个类型约束 (where T : Agent):

    class ADrawer : Drawer 
    { 
        public void Draw<T>(List<T> list) where T : Agent
        { 
            foreach (var a in list) 
            { 
                DrawA(a); 
            } 
        }
        public void DrawA(Agent a) 
        { 
            //draw code here 
        } 
    } 
    

    没有该约束,将a 传递给DrawA 是不合法的,因为aT 类型的引用,并且没有约束就不会从T 类型隐式转换为@ 类型987654329@.

    AnotherDrawer 类非法使用了== 运算符。不能将== 运算符应用于Tint 类型的操作数。您可以通过使用 object.Equals 覆盖来解决这个问题。

    最后,基类出错了,因为它是一个包含抽象成员的非抽象类。

    然而,一般来说,这段代码表明应该是通用的,而不是方法

    abstract class Drawer<T>
    {
        public abstract void Draw(T type);
    }
    

    派生类#1

    class ADrawer : Drawer<List<Agent>>
    {
        public override void Draw(List<Agent> list)
        {
            foreach (var a in list)
            {
                DrawA(a);
            }
        }       
    
        public void DrawA(Agent a)
        {
            //draw code here
        }
    }
    

    派生类#2

    class AnotherDrawer : Drawer<int>
    {
        public override void Draw(int number)
        {
            if (number == 1)
            {
                //draw code
            }
        }
    }
    

    要跟进 Eric Lippert 的评论,这也是我对您的问题的第一反应,您可以考虑采用这种设计:

    abstract class Drawer<T>
    {
        public abstract void Draw(T type);
        public void DrawMany(IEnumerable<T> types)
        {
            foreach (var t in types)
                Draw(t);
        }
    }
    

    派生类#1

    class ADrawer : Drawer<Agent>
    {
        public override void DrawA(Agent a)
        {
            //draw code here
        }
    }
    

    派生类 #2 不变。

    【讨论】:

    • 非常感谢您提供非常有用的答案。我现在已经制作了基类“Abstract”,一切都可以编译了。我现在唯一的问题是,当我创建一个抽屉列表时,我现在必须指定一个类型(例如List&lt;Drawer&lt;Type&gt;&gt; drawerList)。我能说什么是通用的“类型”,或者有什么办法解决这个问题??
    • @Whiplash450 你想要一个抽屉列表,抽屉在哪里绘制不同类型的对象?如果是这样,看看这个问题和我的回答:stackoverflow.com/q/9115593/385844。有几种可能的解决方案,但没有一个像您希望的那样巧妙。
    • @Whiplash450 最好的解决方案在一定程度上取决于您要绘制对象的上下文。它们是否也在您正在迭代的集合中(在这种情况下,对对象的引用将是一个通用的基本类型)?还是您对某个对象有更具体的静态引用,并且您需要为该引用获取正确的抽屉?
    • 分离抽屉的要点是,它们每个人都有一个单独的责任来绘制一个元素类型(即代理、节点等)。这些抽屉采用在运行时传递的对象列表。但是还有其他抽屉没有,只是通过一个 int(例如)。我想将所有抽屉保存在一个列表中,以便它们都可以在一个循环中更新。
    • 由于它们在 Draw 方法中采用不同的参数,因此必须单独进行绘制调用(我认为这是不幸的但不可避免的)。由于基础 Drawer 类现在是“类型化”的,这是否意味着存储所有抽屉的唯一方法是在自定义 List 类中?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-14
    • 1970-01-01
    • 2021-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多