考虑这种情况:
public class Base
{
public int i;
}
public class Sub : Base
{
public void foo() { /* do stuff */}
}
然后我想,给定Base
的实例获取Sub
的克隆实例(在这种情况下i = 17),以便我可以在子类中调用foo
。
Base b = new Base { i=17 };
Sub s = CloneAndUpcast(b);
s.foo();
但是,如何创建CloneAndUpcast
?
我认为应该可以使用反射递归克隆所有Base
- 成员和属性。但是有一些工作。
任何有更好,更整洁的想法的人?
PS。我正在考虑使用它的场景是一组树状结构中的“简单”类(这里没有循环图或类似的),所有类都是简单的值持有者。计划是有一个包含所有值的愚蠢层,然后是一组类似的类(子类),它们实际上包含了值持有者不应该知道的一些业务逻辑。一般不好的做法是的。我认为它适用于这种情况。
答案 0 :(得分:13)
您可以使用AutoMapper来避免编写复制构造函数的乏味。
public class MyClass : MyBase
{
public MyClass(MyBase source)
{
Mapper.Map(source, this);
}
}
并且您需要在应用程序启动时运行一次
Mapper.CreateMap<MyBase, MyClass>();
下载AutoMapper
答案 1 :(得分:7)
这是一种方式(在许多可能性中),你可以做一些像你问的事情。我不确定这是非常漂亮的,并且可能有点难以调试,但我认为它有效:
class BaseClass
{
public int i { get; set; }
public BaseClass Clone(BaseClass b)
{
BaseClass clone = new BaseClass();
clone.i = b.i;
return clone;
}
}
class SubClass : BaseClass
{
public int j { get; set; }
public void foo() { Console.WriteLine("in SubClass with value of i = {0}", i.ToString()); }
}
class Program
{
static void Main(string[] args)
{
BaseClass b1 = new BaseClass() { i = 17 };
BaseClass b2 = new BaseClass() { i = 35 };
SubClass sub1 = CloneAndUpcast<SubClass>(b1);
SubClass sub2 = CloneAndUpcast<SubClass>(b2);
sub1.foo();
sub2.foo();
}
static T CloneAndUpcast<T>(BaseClass b) where T : BaseClass, new()
{
T clone = new T();
var members = b.GetType().GetMembers(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance);
for (int i = 0; i < members.Length; i++)
{
if (members[i].MemberType== MemberTypes.Property)
{
clone
.GetType()
.GetProperty(members[i].Name)
.SetValue(clone, b.GetType().GetProperty(members[i].Name).GetValue(b, null), null);
}
}
return clone;
}
}
基本上,正如您所建议的那样,您使用反射来遍历对象的属性(我将i
和j
设置为公共属性)并在克隆对象中相应地设置值。关键是使用泛型来告诉CloneAndUpcast你正在处理什么类型。一旦你这样做,它就非常简单。
希望这会有所帮助。祝你好运!
答案 2 :(得分:3)
按照&#34;四人帮&#34; :&#34;赞成作品而非继承&#34;这是完美的理由...
如果我们有一个看起来像这样的SuperClass:
public class SuperClass : Person
SuperClass可以轻松地修饰Person类,添加Person类中找不到的属性。 但是,如果Superclass装饰仅用于GUI,会发生什么?例如,bool值表示&#34; Selected&#34;。我们仍然能够从列表中的DB中获取所有人员,但是我们在尝试创建超类并合并数据库结果时遇到了麻烦。
foreach( var person in myPersonList){
var sc = new SuperClass();
sc.Selected = false;
sc=person;
}
编译器抱怨因为Superclass不是编译器的Person,它是Superclass。填写Person子类的属性的唯一方法是迭代并设置每个子类......就像这样。
SuperClass.Name = Person.Name;
SuperClass.Id = Person.ID;
确实非常乏味。但是有更好的方法......不要让超级类继承人
public class SuperClass{
public Person ThisPerson {get;set;}
public bool Selected {get;set;}
}
这给了我们&#34;遏制&#34; Superclass现在包含一个Person类。
现在我们可以这样做:
foreach(var person in MyPersonList){
var sc = new Superclass();
sc.Selected = false;
sc.Person = person;
}
此类的消费者现在必须限定超类/人的属性,如此...
forach(var sc in MySuperClassList){
var selected = sc.Selected;
var name = sc.Person.Name;
}
这样做的好处是,将来您可以添加任何其他容器,它不会影响任何其他容器。您还可以将超类变形为它包含的任何内容。如果每个包含的类成为接口,那么这就是未来的一步。
答案 3 :(得分:1)
好吧,由于b
不是Sub
,我们无法将其“克隆”为一个。
如果Base
具有构造函数和公共属性的适当组合,以使Sub
中的构造函数确保其基础因此具有与b
相同的状态,那么我们可以使用
我想我会绕过整个事情。如果我们关心的是s
在b
的基础上具有相同的状态,并且它没有我们将关心的其他状态(否则我们必须通过它一直到CloneAndUpcast
方法),那么我们需要s
吗?
静态方法可能需要Base
,我们可以使用static public void foo(Base bc)
。我们甚至可以将其定义为扩展方法static public void foo(this Base bc)
,然后将调用编码为b.foo()
。唯一不允许我们这样做CloneAndUpcast()
让我们做的就是访问受保护的成员。
答案 4 :(得分:1)
克隆是一种不好的做法,你的问题是其原因(子类克隆)。 通常,您应该只使用复制cotrs并让子类接受父项作为参数。
public Base(){}
public Base(Base pSource){}
public Sub(){}
public Sub(Base pSource,其他参数......){}
public Sub(Sub pSource){}