在子类中展开父列表

时间:2014-01-22 17:33:42

标签: c# inheritance

我不确定在C#中建模的最佳方式是什么,我将尝试解释我想要做什么。我正在连接到SOAP API(我无法控制),它返回几种类型的实体。所有实体都有一些共享字段,某些实体特定类别的某些共享字段。实际上,任何常规继承结构都有几个级别。

当我通过API请求实体时,无论类型如何,我都必须发送我想要的字段列表和实体类型,然后返回一个通用的Entity对象,其中包含这些字段的值列表。

我想在我的另一个系统中重新创建原始类,保留继承结构。我希望我的类有一个特定于它们的字段列表以及特定于它们继承的父类的字段列表。像这样:

public class BaseEntity 
{
    public virtual IEnumerable<string> Fields
    {
       get { return new List<string> {"field1"}; }
    }
}

public class ChildEntity:BaseEntity 
{
    public virtual IEnumerable<string> Fields
    {
        get 
        { 
            var fields = new List<string> {"field2"}; 
            fields.AddRange(base.Fields);
            return fields;
        }
    }
}

这样的事情会或多或少地起作用,考虑到子类删除父字段的风险(但是部分地通过不假设您不能在属性中具有空值并处理该字段来进行整理) )。这就是请求部分的样子:

ApiRequest request = new ApiRequest()
request.Id = "1";
request.Type = "ChildEntity";
request.Fields = ChildEntity.Fields // This is what I want to get dynamically somehow
RemoteEntity remoteEntity = apiClient.Request(request);
ChildEntity childEntity = new ChildEntity(remoteEntity); // It parses the expected values

但是,字段列表实际上应该是静态的。我想在向服务器请求字段之前访问它,所以我没有任何类实例,因此我不能使用这个继承解决方案。你会建议什么?

3 个答案:

答案 0 :(得分:0)

以下是一些方法:

1:明确定义字段继承关系,la:

public class BaseEntity
{
    protected static readonly string[] fields = { "field1" };
    public static IEnumerable<string> Fields
    {
        get { return fields; }
    }
}

public class ChildEntity : BaseEntity
{
    protected static readonly string[] fields = { "field2" } ;
    public static IEnumerable<string> Fields
    {
        get { return BaseEntity.Fields.Concat(fields); }
    }
}

public class GrandChildEntity : ChildEntity
{
    protected static readonly string[] fields = { "field3" } ;
    public static IEnumerable<string> Fields
    {
        get { return ChildEntity.Fields.Concat(fields); }
    }
}

// Result:
// BaseEntity.Fields == { "field1" };
// ChildEntity.Fields == { "field1", "field2" };
// GrandChildEntity.Fields == { "field1", "field2", "field3" };

不是那么漂亮或超级高效,但比在每个班级中明确定义fields的整个列表要好一些。

2:使用反射直接获取每个类中存在的属性列表,并删除API对象模型中实际不是字段的属性:

public class BaseEntity
{
    private static readonly string[] fields = System.Reflection.MethodInfo.GetCurrentMethod().DeclaringType.GetProperties().Select(p => p.Name).Where(name => !name.Equals("Fields")).ToArray();
    public static IEnumerable<string> Fields { get { return fields; } }
    public object field1 { get; set; }
}

public class ChildEntity : BaseEntity
{
    private static readonly string[] fields = System.Reflection.MethodInfo.GetCurrentMethod().DeclaringType.GetProperties().Select(p => p.Name).Where(name => !name.Equals("Fields")).ToArray();
    public static IEnumerable<string> Fields { get { return fields; } }
    public object field2 { get; set; }
}

public class GrandChildEntity : ChildEntity
{
    private static readonly string[] fields = System.Reflection.MethodInfo.GetCurrentMethod().DeclaringType.GetProperties().Select(p => p.Name).Where(name => !name.Equals("Fields")).ToArray();
    public static IEnumerable<string> Fields { get { return fields; } }
    public object field3 { get; set; }
}

答案 1 :(得分:0)

  

我想在我的其他系统中重新创建原始类

Visual Studio可以通过Add Service Reference完成该操作。它自动从目标Web服务生成代理类。这样可以避免你自己做这项工作。

只需右键单击项目,然后选择“添加服务引用”。对话框大多是不言自明的。

This link是一个简短的操作方法(它提到了WCF,但您可以忽略它,对所有Web服务都是一样的。)

答案 2 :(得分:0)

我最终决定为此使用自定义属性。我认为这是一个更优雅和可维护的解决方案,因为我没有子类覆盖父字段的风险。

[EntityFields("field1","field2")]
public class BaseEntity 
{
    public ISet<string> Fields
    {
        get
        {
            Type typeToCheck = GetType();
            ISet<string> fields = new HashSet<string>();

            do
            {
                var entityFieldsAttribute =
                    (EntityFields) Attribute.GetCustomAttribute(typeToCheck, typeof (EntityFields));
                if (entityFieldsAttribute != null)
                {
                    fields.UnionWith(entityFieldsAttribute.Fields);
                }
                typeToCheck = typeToCheck.BaseType;
            } while (typeToCheck != typeof (BaseEntity));

            return fields;
        }
    }
}

[EntityFields("field3","field4")]
public class ChildEntity:BaseEntity 
{

}