从基类访问继承类的属性

3

我有一个基类和两个派生类,这两个派生类也共享着两个属性。我想从基类中访问这些属性。怎么做?以下是示例:

public abstract class ClientEngine
{
        public void SaveFile(string fileName);
        {
            //Not implemented, I get error here because I don't know how to access Inherited class properties
            if (FileStorage != null))
                File.WriteAllBytes(fileName, FileStorage);
        }
}

    public interface IEngineEntity
    {
        byte[] FileStorage { get; set; }
    }

public class MyEntity1 : ClientEngine, IEngineEntity
{

public string MyProperty1 {get; set;}
public string MyProperty2 {get; set;}

public byte[] FileStorage { get; set; }
}

public class MyEntity2 : ClientEngine, IEngineEntity
{

public string MyProperty3 {get; set;}
public string MyProperty4 {get; set;}

public byte[] FileStorage { get; set; }
}

2
你正在寻找一把薄金属尺子:http://blogs.msdn.com/b/ericlippert/archive/2003/11/03/a-parable.aspx 请认真考虑您所收到的有关设计的反馈,并仔细思考将OO的概念弯曲到您的意愿是否真正是正确的答案。 - Bryan Watts
3个回答

5
如果您需要从基类访问属性,那么您正在做一些错误的事情。请记住这一点,以备将来设计之需。
由于两个继承类都有它,因此您应该在基类中声明FileStorage
如果您不想定义FileStorage的特定类型(比如说byte[]),您可能希望将其定义为更通用的类型,例如IEnumerable<byte>。或者,您可以将FileStorage声明为一个接口,提供您需要从基类访问的任何行为。
更新:
@ghimireniraj的一个好观察:您还应该尝试从IEngineEntity派生ClientEngine。面向对象设计的底线之一是,如果在所有继承类中复制行为,则应在基类中实现。
但是,您真的想从基类访问某些代码吗?您不应该这样做。但是,如果您真的需要,您总是可以进行转换。
public abstract class ClientEngine
{
    public void SaveFile(string fileName);
    {
        byte[] fileStorage = null;
        if (this is MyEntity1)
        {
            MyEntity1 me1 = (MyEntity1)this;
            fileStorage = me1.FileStorage;
        }
        else if (this is MyEntity2)
        {
            MyEntity2 me2 = (MyEntity2)this;
            fileStorage = me2.FileStorage;
        }
        if (fileStorage != null))
            File.WriteAllBytes(fileName, fileStorage);
    }
}

很恶劣,对吧?但是它应该看起来很糟糕,因为我们试图打破OO设计。

更新

另一种方法是按照Marc Gravell的建议进行。而不是强制转换为具体类型,而是转换为接口。但是,我仍然认为这样做很糟糕:我坚信您可以改善设计。


或者从IEngineEntity派生ClientEngine。 - ghimireniraj
1
@ghimireniraj 很好的发现。面向对象设计的底线之一:如果在所有继承类中复制行为,则应该将其放在基类中。 - Adriano Carneiro
我不能这样做,因为MyEntity1和MyEntity2是使用protobuf-net进行序列化的,并且有理由不将FileStorage放在基类中,而是将其实现为接口。我只是没有粘贴整个代码。所以回到问题,如何在不改变逻辑的情况下从基类访问继承类的属性。 - Tomas
@Tomas 不确定我是否看到protobuf-net和这个设计决策之间的关系。如果您让我知道您想要避免的问题,我可能可以提供建议... - Marc Gravell
2
@downvoter 请评论一下你的负评。让我们让这个社区更具建设性。 - Adriano Carneiro
@Adrian:那么,你能否不从ClientEngine派生MyEntity1和MyEntity2,而是让ClientEngine函数接受一个类型为MyEntityBase的参数,而MyEntity1和MyEntity2从中派生呢? - ghimireniraj

3

实现这个功能的方法是将其作为基类中的抽象成员,并在派生类中进行重写;另一种方法是尝试将 this 强制转换为 IEngineEntity - 如果成功,则通过接口使用它,即:

var engine = this as IEngineEntity;
if(engine == null) throw new InvalidOperationException(
   "All engine implementations must provide an IEngineEntity implementation");
var bytes = engine.FileStorage;

不过,如果这是所有子类的要求,不将其推送到基类似乎有点不寻常。通过将其推送到基类,您就不会忘记实现缺失的成员。


1
是的。1. 在基类中使用抽象成员来强制子类实现它;2. 在基类中使用虚成员为子类提供可选重写;3. 当不是所有子类都应该具有它时,由子类实现的接口。 - DK.

1
另一个功能性的替代方案:
var derivedType = Type.GetType(this.GetType().FullName);
var FileStorage = derivedType.GetProperty("FileStorage", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this) as byte[];
if (FileStorage != null) 
{
    ...
}

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