使用附加属性扩展类

4
我有一个来自外部库的类对象,我想添加一些额外的属性。
假设外部类是:
public class ExternalClass
{
    public string EXproperty1 {get;set;}
    public string EXproperty2 {get;set;}
    public string EXproperty3 {get;set;}

    public ExternalClass(){}
}

我有一个对象列表,它会随着时间的推移而被填充。
List<ExternalClass> listOfExternalClass=new List<ExternalClass>();
listOfExternalClass=GetListOfExternalClass();

我可以通过创建一个新类、添加额外的属性并将外部类作为属性来扩展这个类。
public class NewClass
{
    public ExternalClass ExternalClass {get;set;}
    public string NewProperty1 {get;set;}
    public string NewProperty2 {get;set;}

    public NewClass(){}
    public NewClass(ExternalClass externalClass){
        this.ExternalClass=externalClass;
    }
}

但是,要将原始的外部类列表转换为新类列表,我必须创建一个新的新类列表,并迭代原始列表创建一个新对象并将其添加到列表中,例如:
List<NewClass> listOfNewClass=new List<NewClass>();
foreach(var externalClass in listOfExternalClass)
{
    listOfNewClass.Add(new NewClass(externalClass));
}

我将能够访问外部属性,例如

listOfNewClass.FirstOrDefault().ExternalClass.EXproperty1;

我可以用继承来实现这个吗,还是有更有效的方法?
理想情况下,我希望通过调用属性来结束。
listOfNewClass.FirstOrDefault().EXproperty1;

4
为什么不采用继承?你已经尝试过了吗? - xGeo
这类问题往往会导致多个相同的答案。 - user585968
你可能想阅读Eric Lippert关于单子的博客系列 - OldFart
5个回答

7
这可以通过继承来实现。请考虑以下内容。
//Inherit from our external class
public class NewClass: ExternalClass
{
    //Note we do not have a copy of an ExternalClass object here.
    //This class itself will now have all of its instance members.
    public string NewProperty1 {get;set;}
    public string NewProperty2 {get;set;}

    //If it has parameters include those parameters in NewClass() and add them to base().
    //This is important so we don't have to write all the properties ourself.
    //In some cases it's even impossible to write to those properties making this approach mandatory.
    public NewClass()
    {

    }
}

需要了解的几件事:

  • 你的代码被称为包装器。这是因为它“包裹”另一个类或一组类。
  • 你不能从标记为sealed的类继承。
  • 在C#中,类默认情况下不是sealed的。如果它们被标记为sealed,则开发人员有意阻止你从该类继承。这通常是出于良好的原因。

4
如果你可以扩展External类,那么实现起来就很容易:
public class NewClass: ExternalClass
{
    public string NewProperty1 {get;set;}
    public string NewProperty2 {get;set;}

    public NewClass(){}
    public NewClass(ExternalClass externalClass){
        // you would have to copy all the properties
        this.EXproperty1 = externalClass.EXproperty1;
    }
}

这是我开始做的事情,但要映射的属性太多了。如果外部类中的任何属性更改或添加/删除,这也会成为问题。 - DeclanMcD
啊哈,也许可以做一些完全动态的东西,但那可能会太复杂了:https://dev59.com/Z1DTa4cB1Zd3GeqPL8WU - John Smith
仅返回已翻译的文本:或者自动复制所有属性,请参见:https://dev59.com/rnNA5IYBdhLWcg3wgeOk - John Smith
这就是我最终所做的。问题在于由于某些原因,继承类中有几个字段没有被公开,所以每当出现这种情况时,我只需使用this.EXproperty1 = externalClass.EXproperty1;即可。谢谢。 - DeclanMcD

1
是的,继承是你要寻找的内容:
public class ExternalClass
{
   public string EXproperty1 { get; set; }
   public string EXproperty2 { get; set; }
   public string EXproperty3 { get; set; }

   public ExternalClass() { }
}

public class NewClass:ExternalClass
{            
    public string NewProperty1 { get; set; }
    public string NewProperty2 { get; set; }
    public NewClass() { }          
}

1
如果您希望(或需要)委托代替复制,可以执行以下操作:
public class NewClass
{
    public ExternalClass ExternalClass {get;set;}
    public string NewProperty1 {get;set;}
    public string NewProperty2 {get;set;}

    public string EXproperty1 {get { return this.ExternalClass.EXproperty1; };set{ this.ExternalClass.EXproperty1 = value; }; }
    public string EXproperty2 {get { return this.ExternalClass.EXproperty2; };set{ this.ExternalClass.EXproperty2 = value; }; }
    public string EXproperty3 {get { return this.ExternalClass.EXproperty3; };set{ this.ExternalClass.EXproperty3 = value; }; }

    public NewClass(){}
    public NewClass(ExternalClass externalClass){
        this.ExternalClass=externalClass;
    }
}

+1 这就是我一直在忙着打的,赞美 @Licht 的回答,同时补充了当类被密封时可以做什么。唯一的缺点是缺乏多态性。 - David Culp

1

不要针对特定的类型进行工作,而是针对接口进行工作。

以下展示了外观模式适配器模式的混合使用,将外部数据“转换”为一个明确定义的接口(IDocument),有效地抽象出你正在处理的事物。

示例1:关于接口的查询

这里是你将要处理的类型:

public interface IDocument {
    string Name { get; set; }
}

public interface IMetadata {
    string[] Tags { get; set; }
}

这是您自己的表述,如果需要的话:
public class RichDocument : IDocument, IMetadata {
    public string Name { get; set; }
    public string[] Tags { get; set; }
}

这是用于外部数据的包装器: (混合了门面和/或适配器概念)
public class ExternalClass {
    public string Whatever { get; set; }
}

public class ExternalDocument : IDocument /* only a basic object */ {
    private readonly ExternalClass _class;

    public ExternalDocument(ExternalClass @class) {
        _class = @class;
    }

    public string Name {
        get { return _class.Whatever; }
        set { _class.Whatever = value; }
    }
}

并演示如何使用所有这些:

internal class Demo1 {
    public Demo1() {
        var documents = new List<IDocument> {
            new ExternalDocument(new ExternalClass()),
            new RichDocument()
        };

        foreach (var document in documents){
            var name = document.Name;
            Console.WriteLine(name);

            // see if it implements some interface and do something with it
            var metadata = document as IMetadata;
            if (metadata != null) {
                Console.WriteLine(metadata.Tags);
            }
        }
    }
}

例子2:有关组件的查询

这需要更多的涉及,通过将概念推向以统一的方式处理所有内容,您可以在.NET框架、游戏开发或其他任何地方找到它...

您将使用以下定义进行工作:

public interface IContainer {
    IList<IComponent> Components { get; }
}

public interface IComponent {
    // it can be/do anything
}

一些你会查询的组件:

public interface IDocument : IComponent {
    string Name { get; set; }
}

public interface IMetadata : IComponent {
    string[] Tags { get; set; }
}

你的“内部”类型:

public class Container : IContainer {
    public Container() {
        Components = new List<IComponent>();
    }

    public IList<IComponent> Components { get; }
}

你的“包装器”用于外部数据:

public class ExternalClass {
    public string Whatever { get; set; }
}

public class ExternalContainer : IContainer {
    private readonly List<IComponent> _components;

    public ExternalContainer(ExternalClass @class) {
        _components = new List<IComponent> {new ExternalDocument(@class)};
    }

    public IList<IComponent> Components {
        get { return _components; }
    }
}

public class ExternalDocument : IDocument {
    private readonly ExternalClass _class;

    public ExternalDocument(ExternalClass @class) {
        _class = @class;
    }

    public string Name {
        get { return _class.Whatever; }
        set { _class.Whatever = value; }
    }
}

并且有一个用法示例:

public class Demo2 {
    public Demo2() {
        var containers = new List<IContainer> {
            new ExternalContainer(new ExternalClass()),
            new Container()
        };

        foreach (var container in containers) {
            // query container for some components

            var components = container.Components;

            var document = components.OfType<IDocument>().FirstOrDefault(); 
            if (document != null) {
                Console.WriteLine(document.Name);
            }

            var metadata = components.OfType<IMetadata>().FirstOrDefault();
            if (metadata != null) {
                Console.WriteLine(metadata.Tags);
            }
        }
    }
}

注意事项

继承的问题在于它是一种非常死板的方法,一旦开始使用,到某个时候你想要回退,就很难摆脱它。

通过针对抽象化进行工作,事情更加灵活,事物也解耦合。

以下是两个例子,可能会激励您改变您的方法:

组合优于继承

使用组件


你是对的,组合(或委托)在许多情况下是正确的选择,但是你的类型层次结构为一个简单的问题引入了巨大的复杂性。在实际需要处理不同类型对象的要求之前,始终遵循KISS原则(https://en.wikipedia.org/wiki/KISS_principle)。 - John Smith
你是100%正确的。这是我告诉别人的事情,但大多数时候我自己都忘了去做。 - aybe
1
组合是正确的选择。它既符合SOLID原则,也易于测试。继承会引起许多复杂性和依赖关系。 - Gravity API
这是事实,但对于一些人来说一开始并不显而易见。 - aybe

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