我不理解基类继承。

4

我对继承的理解还比较基础。

我想创建一个基类,并实现IDisposable,然后让其他具有功能的类从该基类继承,以便它们共享一个基类型。

这将允许我将每个对象放入一个列表中,以便在需要清理时一次性处理所有内容。但问题是,在C#中,一个类只能继承一次,否则只能使用接口。

那么,像Thread之类的类如何从我的基类继承,而不必再次编写Thread类呢?

简单点说,我想让已经存在的类继承我选择的基类,而不会失去其现有的功能。也就是所有在项目中使用的类都有一个公共基础,可以汇总为一个对象类型,例如List

这是否可能?


事实是,在C#中,一个类只能继承一次,否则只能使用接口。谁告诉你这个的?实际上,继承树的深度没有实际限制。 - Caius Jard
@CaiusJard 问题是,不幸的是基类对象没有实现IDisposable接口,所以我不能使用List<object>然后使用.Dispose()清理列表中的对象。 - Rakku
@Jamiec 等等,什么?我错过了什么吗?据我所知,List 一次只能接受一个对象类型,难道我错了吗? 即使不是这样,我也必须为每个不同的对象类型使用不同的 foreach 来使用 .Dispose()。 - Rakku
@Rakku,你可以拥有一个类型为接口的列表。请看我的回答。 - Jamiec
@Rakku 这是一个 XY 问题(http://xyproblem.info) - 我仍然认为你询问的是你认为该如何解决问题,而不是你真正面临的实际问题。请阅读一下。 - Jamiec
显示剩余7条评论
3个回答

6

如果您的目标仅是存储可以处理的对象列表,则需要使用List<IDisposable>。任何直接或间接实现该接口的类都可以存储在其中。

var disposables = new List<IDisposable>();
disposables.Add(new MyClass1());
disposables.Add(new SomeClass());
disposables.Add(new AnotherClass());

disposables.ForEach(d => d.Dispose());

我不确定这是否是一个值得的模式!


哇,我不知道我可以只使用接口作为列表类型!这是一个很好的快捷方式。你会使用什么样的模式来清理? - Rakku
不是这样的!事实上,你很少会有一个完全不同的随机类列表,需要一次性清理。无论哪个类实例化对象,都应该负责之后的清理工作。 - Jamiec
啊,现在我知道误解出在哪里了。 我不想使用这个模式来清理完整的类。 该模式用于每个类中,该类清理其实例化的组件。 列表保存组件而不是整个类。 我曾经想过,必须为没有共同父项(已实现IDisposable)的组件创建多个列表。 但是由于现在我可以将所有内容都放入一个仅使用接口的列表中,所以我遇到的问题已经消失了。 - Rakku
@Rakku 如果你找到了解决问题的答案,请将一个答案标记为已接受。 - Michał Turczyn

3
您提供的示例涉及接口。在C#中,一个类可以实现多个接口。
另一方面,在C#中,一个类只能继承一个类
话虽如此,并不总是好主意从类中继承,因为它非常具有限制性。相反,我建议使用组合: wiki 例如,我建议使用组合设计模式(这是一个小差异,因为复合体没有对象集合,而仅有一个对象):
public interface IFoo
{
    void DoFooStuff();
}

public class Foo : IFoo
{
    public void DoFooStuff() {...};
}

public class ShouldImplementFoo : IFoo
{
    // Composition, you need to initialize it some time
    private Foo _foo;
    
    public void DoFooStuff() { _foo.DoFooStuff(); }
}

编辑

重新思考了您的问题后,您似乎想要将一些不同类别的对象放入列表中,以便于对列表中每个对象执行某个定义好的操作

最简单的方法是:使用您定义的操作定义接口:

public interface IMyIntf
{
    void MyDesiredAction();
}

现在,让所有类都实现它。您可能会想:“好了,现在我需要到处实现这个方法......”,但事实并非如此!只需使用适配器设计模式,例如,有一个类:

public class MyConcreteClass
{
    public void ActionIWishToExecuteOnThisObject() 
    { ... }
}

你只需要将其修改为:
public class MyConcreteClass : IMyIntf
{
    public void ActionIWishToExecuteOnThisObject() 
    { ... }

    // implement interface
    public void MyDesiredAction()
    {
        ActionIWishToExecuteOnThisObject();
    }
}

现在,您可以使用一些聚合物,例如List<IMyIntf>,将您感兴趣的所有对象放在那里,并执行以下操作:
foreach(var item in myList)
    item.MyDesiredAction();

1

一个类可以继承自祖先类(或 Object,所有类的根)。

此外,它可以实现一些接口(或不实现任何接口)。

一个类可以有未实现的成员,被称为抽象成员,这样该类就是抽象类。

成员也可以是虚拟的以启用多态性(抽象成员也是如此)。

一旦实现,一个东西在类中可用,如果不是抽象的,则可以真正地被实例化,并且对于所有子类也是可用的,除了那些被定义为私有的,这些在子类中是隐藏的。

不需要重复,这是面向对象编程的优势之一。

通过使用抽象、封装、继承和多态性,在研究我们工作的真实世界和领域后,我们可以设计一个该真实世界的投影,即我们头脑中的视图,其中的概念具有数据(字段和属性)和操作(方法),分别归类为人造物品(对象)和关系、组合、聚合、接口、组件、控件等。

因此,我们可以将处理过程因素化,以避免重复,例如:

  • 有一个Animal类和CatDog子类,
  • 我们在动物中加入了SizeColor方面,
  • 以及为所有动物实现的EatWalk方法,
  • 还有一个未实现的抽象DoSound

因此:

  • 在具体类CatDog中,我们不再重新实现这些内容,
  • 除了对于每个真实动物性别特定的DoSound

关于接口,它们是虚拟合同。

例如,我们可以有毫无共同之处的动物和建筑物,乍一看(我们不会回到原子和光子),但我们希望能够管理对象实例,如猫和狗,以及房屋和博物馆拍照。

为了实现这一点,我们定义了一个接口IPhotoTaking,并在定义前面的类时添加了它们实现了该接口。

代码示例:

public interface IPhotoTaking
{
  Image TakePhoto();
}

public abstract class Animal : IPhotoTaking
{
  public int Size { get; set; }
  public Color Color { get; set; }
  public void Eat() { ... }
  public void Walk() { ... }
  public abstract void DoSOund();
  public Image TakePhoto();
}

public class Cat : Animal // no need to repeat IPhotoTaking
{
  public override void DoSound() { ... }
}

public class Dog : Animal
{
  public override void DoSound() { ... }
}

public abstract class Building :IPhotoTaking
{
  public Image TakePhoto();
}

public class House : Building
{
}

public class Museum : Building
{
}

现在我们有了这个列表,我们可以像这样使用多态性:
var animals = new List<Animal>();

foreach ( var animal in animals )
  animal.DoSound();

同时,由于 C# 尚不支持在 List<> 上应用钻石操作符的真正多态性,因此我们可以像这样使用接口:

var items = new List<IPhotoTaking>();

foreach ( var item in items )
  item.TakePhoto();
< p > 备注:如果拍照对于每种类型可能不同,我们可以将其设置为虚拟,以便在每个类型中专门实现它。

面向对象编程基本原则

C#中的抽象是什么?

如何在public、private和protected访问修饰符之间选择?

关联、组合和聚合

多态是什么?

接口和类之间的区别是什么?

C#中缺乏真正的泛型多态性和缺少菱形操作符的问题


谢谢,解释非常有帮助,不幸的是我只能标记一个答案,但你的答案也非常好! :) - Rakku
@Rakku 只需选择最能向您解释并帮助您以感觉最佳的方式实现目标的方法,使代码简洁、清晰、简短、易懂、易维护、优雅、健壮、安全且高效,而不是像意大利面一样混乱。 - user12031933

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