2012-01-25 45 views
4

考慮這種情況:從基類創建子類的克隆副本

public class Base 
{ 
    public int i; 
} 

public class Sub : Base 
{ 
    public void foo() { /* do stuff */} 
} 

然後我想,鑑於Base一個實例得到的Sub的克隆實例(與我在這種情況下= 17),使我可以在子類中調用foo

Base b = new Base { i=17 }; 
Sub s = CloneAndUpcast(b); 
s.foo(); 

但是,如何創建CloneAndUpcast

我在想,是應該可以遞歸克隆所有的使用反射Base - 成員和屬性。但相當一些工作。

任何人有更好的,整潔的想法?

PS。我正在考慮使用這個場景是一個樹狀結構中的「簡單」類(這裏沒有循環圖或類似圖),所有類都是簡單的值持有者。計劃是要有一個愚蠢的圖層,它保存着所有的值,然後是一組相似的類(子類),它們實際上包含了價值持有者不應該知道的一些商業邏輯。通常不好的做法是。我認爲它適用於這種情況。

+3

你不能完全按照你所要求的。有很多原因,但即使可以,也足以說,這通常是一種不好的做法。如果您可以給我們提供您想要解決的更廣泛的問題,我們可以幫助您找到正確的模式。 –

+1

這聽起來像是一個具體的問題,表明一個更普遍的問題正在被錯誤地解決。舊鞋和玻璃瓶都不合適。請參閱:http://weblogs.asp.net/alex_papadimoulis/archive/2005/05/25/408925.aspx – FMM

+0

我認爲你試圖達到的目標*可能*表示設計問題。另外,由於'i'對於'base'類是私有的,所以'sub'無論如何都不能使用它,所以根據'Base'的工作原理,不需要複製該字段。 – tobsen

回答

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; 
    } 
} 

基本上,如你所說,你可以使用反射通過對象的屬性來遍歷(我設置ij作爲公共屬性),並在克隆對象中相應地設置值。關鍵是使用泛型來告訴CloneAndUpcast你正在處理的是什麼類型。一旦你這樣做,這非常簡單。

希望這會有所幫助。祝你好運!

+0

一個靜態泛型,T由它的參數定義。那很完美!我得到了一個內部甚至不需要反思的例子;它是由超類定義的列表,其處理根據子類而不同,所以我要做的就是複製它。 – Nyerguds

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()讓我們做的就是訪問受保護的成員。

1

克隆是一個不好的做法,你的問題是(克隆子類)的原因。 一般而言,您應該使用copy cotrs,並讓子類接受父級作爲參數。

公共基地(){}

公共鹼(鹼的PSource){}

公共子(){}

公共子(鹼的PSource,其他參數...){}

公衆子(分的PSource){}

12

你可以使用AutoMapper避免編寫拷貝構造函數的單調乏味。

public class MyClass : MyBase 
{ 
    public MyClass(MyBase source) 
    { 
     Mapper.Map(source, this); 
    } 
} 

,你需要在你的應用程序啓動時運行一次這個

Mapper.CreateMap<MyBase, MyClass>(); 

您可以從https://github.com/AutoMapper/AutoMapper

+0

整潔而精確。這應該是問題的答案。 –

3

下載AutoMapper每對「四人幫」:「有利於對繼承組成」這是一個完美的理由......

如果我們有一個SuperClass,看起來像這樣:

public class SuperClass : Person 

SuperClass可以很容易地裝飾Person類,添加Person類中找不到的屬性。 但是,如果超類裝飾僅用於GUI,會發生什麼情況?例如指示「選定」的布爾值。我們仍然可以從列表中獲得所有人員,但是我們遇到了麻煩,試圖創建超類併合並數據庫結果。

foreach(var person in myPersonList){ 
    var sc = new SuperClass(); 
    sc.Selected = false; 
    sc=person; 
} 

編譯器抱怨,因爲超類不是編譯器的人,它是超類。填充Person子類的屬性的唯一方法是迭代並設置每一個......就像這樣。

SuperClass.Name = Person.Name; 
    SuperClass.Id = Person.ID; 

的確很乏味。但是有一個更好的辦法....不要超類從Person

public class SuperClass{ 
    public Person ThisPerson {get;set;} 
    public bool Selected {get;set;} 
} 

繼承這給了我們「遏制」超現在包含一個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; 
} 

這樣做的好處是,在將來,您可以添加任何其他您想要的容器,並且不會影響任何其他容器。你也可以將超類變形爲它包含的任何東西。如果每個包含的類都成爲接口,那麼這就是更進一步的道路。