C# 如何基于父类实例生成子类构造函数?
我有一个Item和一个子类AdvancedItem(如果重要的话,它们都由值类型组成): 独立创建项目或AdvancedItem很容易:C# 如何基于父类实例生成子类构造函数?,c#,inheritance,constructor,clone,base-class,C#,Inheritance,Constructor,Clone,Base Class,我有一个Item和一个子类AdvancedItem(如果重要的话,它们都由值类型组成): 独立创建项目或AdvancedItem很容易: var item = new Item { A = "aa", B = true, C = 'c', ... }; var aItem = new AdvancedItem { A = "aa", B = true, C = 'c', ..., Z = "zz" }; 现在,我只想通过分别为一个项目提供字符串Z将其转换为AdvancedItem。为了实现这一
var item = new Item { A = "aa", B = true, C = 'c', ... };
var aItem = new AdvancedItem { A = "aa", B = true, C = 'c', ..., Z = "zz" };
现在,我只想通过分别为一个项目提供字符串Z将其转换为AdvancedItem。为了实现这一点,我考虑使用构造函数
尝试一个:
// annoying, we are not using the inheritance of AdvancedItem:Item
// so we will need to edit this whenever we change the class Item
public AdvancedItem(Item item, string z)
{
A = item.A;
B = item.B;
...;//many lines
Z = z;
}
尝试B:
// to use inheritance it seems I need another constructor to duplicate itself
public Item(Item item)
{
A = item.A;
B = item.B;
...;//many lines
}
public AdvancedItem(Item item, string z) : base(Item)
{
Z = z;
}
是否有任何方法可以改进第二次尝试,以避免写入多行X=item.X
?可能是一种自动克隆或自动复制类的解决方案,其中公共项(item项)
将写入一行?考虑使用它在对象之间复制属性
这将允许:
Item a = new Item { A = 3, B = 'a', .... };
AdvancedItem advanced= Mapper.Map<AdvancedItem>(a);
string z = "Hello World";
advanced.Z = z;
我们可以定义一个创建字段复制表达式的方法。由于您提到所有字段都是值类型,我们可以将每个字段逐个复制到另一个对象:
private static void MapFields<T>(T target, T source)
{
Type type = typeof (T);
if (!Mappers.ContainsKey(type))
{
//build expression to copy fields from source to target;
var targetParam = Expression.Parameter(typeof(object));
var targetCasted = Expression.TypeAs(targetParam, typeof(T));
var sourceParam = Expression.Parameter(typeof(object));
var sourceCasted = Expression.TypeAs(sourceParam, typeof(T));
var setters = new List<Expression>();
//get all non-readonly fields
foreach (var fieldInfo in typeof(T).GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).Where(f => !f.IsInitOnly))
{
Expression targetField = Expression.Field(targetCasted, fieldInfo);
Expression sourceField = Expression.Field(sourceCasted, fieldInfo);
setters.Add(Expression.Assign(targetField, sourceField));
}
Expression block = Expression.Block(setters);
var mapperFunction = Expression.Lambda<Action<object, object>>(block, targetParam,
sourceParam).Compile();
Mappers[type] = mapperFunction;
}
Mappers[type](target, source);
}
private static readonly Dictionary<Type, Action<object, object>> Mappers =
new Dictionary<Type, Action<object, object>>();
请注意此代码比AutoMapper更复杂,可靠性更低,不推荐或不适合生产系统 我会这样做。对私有构造函数使用静态工厂方法,并在基类中定义副本构造函数
AdvancedItem
{
Public Static AdvancedItem FromItem(Item i, string z)
{
AdvancedItem item = new AdvancedItem(i);
item.Z = z;
return item;
}
private AdvancedItem(Item i) : Base(i) {}
}
然后用法是
AdvancedItem i=AdvancedItem.FromItem(item,extraThing)代码>是的,根据Bas的建议,您可以使用Automapper,但同时还有另一种选择,即使用奥的斯。我知道Automapper比奥的斯更好,但可以将其视为一种选择
请查看:-
如官方网站所述:-
Otis是一个.Net对象转换库,即对象到对象
制图员
它会自动生成转换器程序集或类
将一种类型的实例转换为另一种类型的实例。这些
可以使用属性在类型元数据中描述转换,或者
单独在XML源(文件、字符串、数据库)中
奥的斯旨在解决一些常见的设计和维护问题
实施任务,例如,简化对
DTO类(此处更多),或转换业务域类型实例
对于表示层实例,但更一般地说,可以使用它
需要在不同类型之间进行转换的任何位置
奥的斯无需手动实施类型转换器
为类层次结构实现此类功能的一种面向对象方法是在基类中引入受保护的副本构造函数(尽管它仍然要求您编写所有赋值):
然后从派生类调用它,如下所示:
public class AdvancedItem : Item
{
public AdvancedItem(Item item, string z): base(item)
{
Z = z;
}
public string Z;
}
请注意,这种方法不会阻止您编写所有赋值行,但您只需要编写一次,这确实意味着您现在有了一个受保护的副本构造函数,这可能非常有用。此外,作业现在都在它们所属的基类中
这种方法可以扩展到其他派生类。您可以将受保护的复制构造函数引入以公共构造函数编写的AdvancedItem
(以避免重复代码)
例如:
public class AdvancedItem : Item
{
protected AdvancedItem(AdvancedItem other): this(other, other.Z)
{
}
public AdvancedItem(Item item, string z): base(item)
{
Z = z;
}
public string Z;
}
public class EvenMoreAdvancedItem: AdvancedItem
{
public EvenMoreAdvancedItem(AdvancedItem advancedItem, double q): base(advancedItem)
{
Q = q;
}
public double Q;
}
考虑到您真正想要做的是为不同的服务序列化对象的不同部分,我将使用组合而不是继承
所以你会:
public Item
{
public string A;
public bool B;
public char C;
...// 20 fields
}
public AdvancedItem
{
[DataMember]
public Item baseItem;
[DataMember]
public string Z;
}
public Item(Item item)
{
A = item.A;
B = item.B;
...;//many lines
}
public AdvancedItem(Item item, string z)
{
baseItem = item; // or item.Clone();
Z = z;
}
我想知道你为什么要把一个项目变成一个高级项目。这似乎是一个奇怪的设计选择。@mdebeus事实是,在我的例子中,不同的序列化有不同的[DataMember()]
属性。我甚至没有添加新字段,但我希望根据所使用的Web服务而有不同的[DataMember()]
。也许还有另一种方法可以实现条件DataMemberAttribute。这很好,但对于我的库,我将无法包含AutoMapper(尝试使用尽可能少的来自外部代码的依赖项,以使库尽可能小)。它听起来并不比AutoMapper复杂:我在AutoMapper上运行了git diff--stat`git hash object-t tree/dev/null`,它在741个文件上返回了149053个插入。但比使用AutoMapper更复杂;)+1因为您提醒我通过组合来保护副本构造函数,所以不再需要构造函数公共项(Item Item)
。但是,在使用DataContractSerializer或DataContractJsonSerializer时,如何使用您的技术实现平面序列化?像{“A”:“aa”,“B”:true,…,“Z”:“zz”}
?我不想要{“baseItem”:{“A”:“aa”,“B”:true,…},“Z”:“zz”}
为什么要关心序列化数据是否是平面的。序列化程序旨在序列化/反序列化一个类,这最终是您将要使用的。除了将序列化数据反序列化回对象之外,您是否计划对序列化数据执行其他操作?此外,我不会完全忽略项(Item)构造函数。复制构造函数在处理对象时非常有用,因为它们不想更改Web服务,所以我必须直接处理它(xml和json)。
public class Item
{
protected Item(Item other)
{
this.A = other.A;
this.B = other.B;
this.C = other.C;
}
public string A;
public bool B;
public char C;
// 20 fields
}
public class AdvancedItem : Item
{
public AdvancedItem(Item item, string z): base(item)
{
Z = z;
}
public string Z;
}
public class AdvancedItem : Item
{
protected AdvancedItem(AdvancedItem other): this(other, other.Z)
{
}
public AdvancedItem(Item item, string z): base(item)
{
Z = z;
}
public string Z;
}
public class EvenMoreAdvancedItem: AdvancedItem
{
public EvenMoreAdvancedItem(AdvancedItem advancedItem, double q): base(advancedItem)
{
Q = q;
}
public double Q;
}
public Item
{
public string A;
public bool B;
public char C;
...// 20 fields
}
public AdvancedItem
{
[DataMember]
public Item baseItem;
[DataMember]
public string Z;
}
public Item(Item item)
{
A = item.A;
B = item.B;
...;//many lines
}
public AdvancedItem(Item item, string z)
{
baseItem = item; // or item.Clone();
Z = z;
}