假设我有以下继承树:
______ Object______
/ \
Food Material
/ \ / \
Egg Carrot Box Axe
在C#中表达如此
class Object { ... }
class Food : Object { ... }
class OfficeMaterial : Object{ ... }
class Box : OfficeMaterial { ... }
class Spoon : OfficeMaterial { ... }
class Egg : Food { ... }
class Carrot : Food { ... }
现在,我想在 Box 和 Egg 之间共享仅功能,例如:
bool opened = true;
public void Open() { open = true; }
public void Close() { open = false; }
public bool IsOpen() { return opened; }
这是一个简短的例子,但它可能会更长。
这样做的正确方法是什么?如果我使用继承,其他类如 Carrot 和 Ax 将获得它,这是我不希望的。
更新
很多人提到Composition
。有人能告诉我在我提出的例子中它会如何起作用吗?
答案 0 :(得分:2)
您有几个选择:
前:
public interface IOpenable
{
void Open();
void Close();
bool IsOpen();
}
然后任何需要openable / closable行为的类都可以相应地实现接口。
答案 1 :(得分:0)
为什么不使用界面? IOpenable
?
interface IOpenable {
void Open();
void Close();
bool IsOpen();
}
答案 2 :(得分:0)
当我考虑Egg
和Box
之间不是Carrot
和Axe
共享属性的相似之处时,我首先想到的是它们包含的内容。< / p>
Egg
和Box
有Contents
可以曝光,隐藏,添加,删除等。请注意此处的语言 - Egg
和Box
Contents
。 一词(或有)表示应将object
代表另一个对象的Contents
用作组合元素。
Egg
是{/ 1}}的类型。 Food
Egg
为Contents
和Yolk
。
EggWhite
是类型的Box
。 OfficeMaterial
Box
为Contents
。
我可以这样写:
OfficeSupplies
这将进一步允许我:
public class Food : Object
{
public void Eat() { }
}
public class OfficeMaterial : Object { }
public class Contents : Object
{
bool _visible = false;
List<Object> _things = new List<Object>();
public int Count { get { return _things.Count; } }
public bool IsOpen { get { return _visible; } }
public void Expose()
{
_visible = true;
}
public void Hide()
{
_visible = false;
}
public void Add(object thing)
{
_things.Add(thing);
}
public bool Remove(object thing)
{
return _things.Remove(thing);
}
}
public class Box : OfficeMaterial
{
public Contents BoxContents = new Contents();
}
public class Egg : Food
{
public Contents EggContents = new Contents();
}
然后我想
void PlaceEggsInBox(int numEggs, Box box)
{
box.BoxContents.Expose();
if (box.BoxContents.AreVisible)
{
int eggsInBox = box.BoxContents.Things.Count(thing => thing is Egg);
for (int i = 0; i < numEggs - eggsInBox; i++)
{
box.BoxContents.Add(new Egg());
}
}
}
void EatAllEggsInBox(Box box)
{
box.BoxContents.Expose();
foreach (Egg egg in box.BoxContents.Things.Where(thing => thing is Egg))
{
box.BoxContents.Remove(egg);
egg.EggContents.Expose();
if (egg.EggContents.AreVisible) egg.Eat();
}
}
方法是Eat()
类的方法。 Food
方法是Expose()
类的方法,不是Contents
类或Food
类,也不是OfficeMaterials
也不是Egg
类。
答案 3 :(得分:0)
实际上是多重继承,C#除了通过接口以外不支持。
一个选择是创建一个没有 接口的Open方法(否则,您将不得不实现它,因此这仅用于输入),然后在一个静态类中实现一个静态扩展方法您的界面。
public Interface IOpenable { }
public static class IOpenableExtensions {
public static Open(this IOpenable obj){
// work on any concrete implementation of IOpenable
}
}
用法:
var box = new Box; // Box implements IOpenable
var egg = new Egg; // Egg implements IOpenable
box.Open();
egg.Open();
当实际继承反映核心结构或组织,而接口提供某些对象共享的公共“行为”时,此模式可能会很好地工作。一个类可以实现一个或多个这样的接口。
C#8默认接口实现可能会使这种模式更加普遍。
实际上,在使用处理基类的函数时,您通常最终需要将对象强制转换为接口。
public IEnumerable<Food> GetFoods(){ ... }
public void HandleFood() {
for(var food in GetFoods()) {
if(food is IOpenable openableFood){
openableFood.Open();
}
}
}
最后,请注意,这不是真正的多重继承,因为您无法通过接口层次结构继承/覆盖扩展方法本身。如果您同时拥有两个Open()扩展方法的IOpenableFood继承IOpenable,则不能在派生方法中调用base(),而必须选择要显式使用的扩展方法。最好完全避免这种情况。
var egg = new Egg(); // Egg inherits both IOpenable and IOpenableFood
egg.Open(); // Error: Ambiguous method
IOpenableFoodExtensions.Open(egg); // OK, call that method
IOpenableExtensions.Open(egg); // OK, call other method