2015-09-24 142 views
3

中的服務(,我不能改變)我有兩個對象類BarBaz與大多相似特性(但遺憾的是沒有,他們不會從同一派生的構造函數基類或來自同一界面...是啊繼承 - 啞),以及與它們相對BarQuxBazQux性能的相關性類:使擴展方法/包裝類通用

public class Bar       public class Baz 
{           { 
    public int ID { get; set; }    public int ID { get; set; } 
    public bool Active { get; set; }   public bool Active { get; set; } 
    public int BarQux { get; set; }   public int BazQux { get; set; } 
    ...          ... 
}           } 

public class Qux 
{ 
    public int ID { get; set; } // Corresponds to BarQux and BazQux 
    public string Name { get; set; } 
} 

在WPF屏幕,我綁定列表每種類型(BazBar )到兩個獨立的ListViews。我需要每個人都有一個額外的Selected CheckBox列。要做到這一點,我創建與公共屬性的包裝類,附加Selected屬性,構造每個:

public class Foo 
{ 
    public Foo(Bar bar, Qux qux) 
    { 
     this.Active = bar.Active; 
     this.FooQux = string.Format("{0} - {1}", qux.ID, qux.Name); 
     ... 
    } 

    public Foo(Baz baz, Qux qux) 
    { 
     this.Active = baz.Active; 
     this.FooQux = string.Format("{0} - {1}", qux.ID, qux.Name); 
     ... 
    } 

    public bool Selected { get; set; } 
    public int ID { get; set; } 
    public bool Active { get; set; } 
    public string FooQux { get; set; } 
    ... 
} 

Baz類和Bar的每個集合轉換成的Foo集合,我創建下面的擴展方法:

public static List<Foo> ToFoo(this IEnumerable<Bar> bars, IEnumerable<Qux> quxs) 
{ 
    List<Foo> foos = new List<Foo>(); 

    foreach (Bar bar in bars) 
    { 
     Foo foo = new Foo(bar, quxs.Single(qux => qux.ID == bar.BarQux)); 
     foos.Add(foo); 
    } 

    return foos; 
} 

public static List<Foo> ToFoo(this IEnumerable<Baz> bazs, IEnumerable<Qux> quxs) 
{ 
    List<Foo> foos = new List<Foo>(); 

    foreach (Baz baz in bazs) 
    { 
     Foo foo = new Foo(baz, quxs.Single(qux => qux.ID == baz.BazQux)); 
     foos.Add(foo); 
    } 

    return foos; 
} 

問:

如何讓我這個普通的?

理論,實施和錯誤:

  1. 由於構造函數是除了BarBaz參數幾乎是相同的,我可以以某種方式使用泛型類型T,使一個構造函數,仍然搶特性?

    public class Foo<T> 
    { 
        public Foo(T obj, Qux qux) 
        { 
         this.Active = obj.Active; // 'T' does not contain a definition for 'Active'... 
         this.Qux = string.Format("{0} - {1}", qux.ID, qux.Name); 
         ... 
        } 
        ... 
    } 
    
  2. 更改構造函數接收Qux對象的全部收集和做quxs.Single(qux => qux.ID == object.ObjectQux)邏輯存在。然後將擴展方法變成一個通用方法,如下所示。

    public static List<Foo> ToFoo<T>(this IEnumerable<T> objCollection, IEnumerable<Qux> quxs) 
    { 
        List<Foo> foos = new List<Foo>(); 
    
        foreach (T obj in objCollection) 
        { 
         Foo foo = new Foo(obj, quxs); // The best overloaded method... has some invalid arguments. 
         foos.Add(foo); 
        } 
    
        return foos; 
    } 
    
  3. 1和2合起來了嗎?任何我沒有想到的事情?

+0

你可能想看看這個https://en.wikipedia.org/wiki/Adapter_pattern – BlackBear

+0

您無法修改該文件或無法修改該文件屁股?例如,你可以實現一個部分類。但是,你將不得不將原來的類改爲部分類。 –

+0

@ScottNimrod我無法修改服務中的任何類或文件,例如'Bar','Baz'和'Type'類。如果我願意,我會很樂意讓前兩個從基類或接口繼承 - 這會讓我在有限的經驗中簡化事情變得更容易。 – OhBeWise

回答

1

如果您的房產數量有限且列表中的物品數量也很少,那麼您可以使用Reflection。既然你會在WPF中使用它,我也會建議把這個過程移到一個單獨的後臺線程中。

通用富

public class Foo<T> 
{ 
    public Foo(T obj, Qux qux) 
    { 
     //this.Active = obj.Active; 

     var fooProps = GetType().GetProperties().ToList(); 
     var tProps = typeof(T).GetProperties() 
      .Where(p => 
      { 
       var w = fooProps.FirstOrDefault(f => f.Name == p.Name); 
       return w != null; 
      }).ToList(); 

     foreach (var propertyInfo in tProps) 
     { 
      var val = propertyInfo.GetValue(obj); 
      fooProps.First(e => e.Name == propertyInfo.Name).SetValue(this, val); 
     } 
     this.FooQux = string.Format("{0} - {1}", qux.ID, qux.Name); 
    } 
    public bool Selected { get; set; } 
    public int ID { get; set; } 
    public bool Active { get; set; } 
    public string FooQux { get; set; } 
} 

你的擴展方法

public static IEnumerable<Foo<Bar>> ToFoo(this IEnumerable<Bar> bars, IEnumerable<Qux> quxs) 
    { 
     return bars. 
      Select(bar => new Foo<Bar>(bar, quxs.Single(qux => qux.ID == bar.BarQux))).ToList(); 
    } 

    public static IEnumerable<Foo<Baz>> ToFoo(this IEnumerable<Baz> bazs, IEnumerable<Qux> quxs) 
    { 
     return bazs. 
      Select(baz => new Foo<Baz>(baz, quxs.Single(qux => qux.ID == baz.BazQux))).ToList(); 
    } 
    public static IEnumerable<Qux> ToQuxes(this IEnumerable<BazQux> bazQuxs) 
    { 
     return bazQuxs.Select(b => new Qux(typeof(BazQux), b)).ToList(); 
    } 

    public static IEnumerable<Qux> ToQuxes(this IEnumerable<BarQux> barQuxes) 
    { 
     return barQuxes.Select(b => new Qux(typeof(BarQux), b)).ToList(); 
    } 

同樣,您也可以將您的BarQuxBazQux到非通用類Qux

public class Qux 
{ 
    public int ID { get; set; } // Corresponds to BarQux and BazQux 
    public string Name { get; set; } 

    public Qux(Type type, object obj) 
    { 
     var ob = Convert.ChangeType(obj, type); 

     var quxProps = GetType().GetProperties(); 
     var obProps = ob.GetType().GetProperties() 
      .Where(p => 
      { 
       var w = quxProps.FirstOrDefault(f => f.Name == p.Name); 
       return w != null; 
      }).ToList(); 
     foreach (var propertyInfo in obProps) 
     { 
      var val = propertyInfo.GetValue(obj); 
      quxProps.First(e => e.Name == propertyInfo.Name).SetValue(this, val); 
     } 

    } 
} 

然後,您可以只調用ToFoo擴展方法,瞧你有富的列表。

你也可以轉換到富非通用使用Qux

+0

寫得很好的建議。我目前正在按照這些方法測試一個想法,儘管我更喜歡你通過屬性循環的方法。我不太瞭解反射,我很好奇這是否太昂貴,因此不值得花費時間 - 這些類共有6個屬性,加上Qux屬性,通常對於任何給定的對象都轉換爲「Foo」客戶將從少數幾個到幾百個不等。思考?我想花一些時間來測試你的建議,然後我會回覆你。 – OhBeWise

+1

查看6個屬性的數量和幾百條記錄的情況,上述解決方案是可行的。您擁有的另一個好處是,它是要使用反射進行處理的客戶端計算機。如果它已經在服務端,您可能會遇到問題,因爲託管該服務的計算機將受到衝擊 – Sandesh

+0

感謝您的輸入!我無法對Qux類進行任何修改,因爲這也是服務的一部分 - 但我不需要它們。通用類「Foo」非常好。我發佈了一個答案,顯示了我的確切用法,使擴展方法也是通用的。 – OhBeWise

0

的邏輯我實在看不出做這種方式的任何好處。

如果你是堅定的關於使用泛型,你可以做一些類型在T檢查,像這樣:

public class Foo<T> 
{ 
    public Foo(T obj, Qux qux) 
    { 
     var baz = obj as Baz; 
     var bar = obj as Bar; 

     Active = baz != null && baz.Active || bar != null && bar.Active; 

     this.FooQux = string.Format("{0} - {1}", qux.ID, qux.Name); 
    } 

    public bool Selected { get; set; } 
    public int ID { get; set; } 
    public bool Active { get; set; } 
    public string FooQux { get; set; } 
} 

或者:

public static List<Foo> ToFoo<T>(this IEnumerable<T> objCollection, IEnumerable<Qux> quxs) 
{ 
    List<Foo> foos = new List<Foo>(); 

    foreach (T obj in objCollection) 
    { 
     var baz = obj as Baz; 
     var bar = obj as Bar; 

     Foo foo = null; 

     if(baz != null) 
      foo = new Foo(baz, quxs.Single(qux => qux.ID == baz.BazQux)); 

     if(bar != null) 
      foo = new Foo(bar, quxs.Single(qux => qux.ID == bar.BarQux)); 

     if(foo != null) 
      foos.Add(foo); 
    } 

    return foos; 
} 

但是,這顯然是比你也好不到哪裏去已經有了,如果T不是Bar或者Baz(沒有顯示),你將不得不做一些異常處理,這會使你面臨潛在的運行時間故障。你也可以使用反射,但也會這樣做。

不幸的是,您正在尋找的是C#不可能的。你可以通過這種方式逃脫的唯一方法是,如果接口是鴨式的Golang,但事實並非如此。

+1

實現包裝器有一個有效的點。當我被告知可能會有更多具有相同屬性名稱和類型的類時,我還有一個應用程序開發中途遇到的情況,這些類也需要注意。編寫更多構造函數是不可能的。所以我通過反思去照顧其他類來節省開發時間 – Sandesh

+0

我對通用實現的興趣在於如果服務類別發生變化,那麼重複代碼的減少和未來的工作就會減少。很有可能是我所擁有的是足夠的,泛型會更少 - 我願意接受 - 這是一種學習體驗。 – OhBeWise

0

按照由Sandesh的建議,最終的結果:

public class Foo<T> 
{ 
    public Foo() {} 

    public Foo(T obj, IEnumerable<Qux> quxs, string quxPropName) 
    { 
     var fooProps = GetType().GetProperties().ToList(); 
     var tProps = typeof(T).GetProperties() 
      .Where(p => 
      { 
       var w = fooProps.FirstOrDefault(f => f.Name == p.Name); 
       return w != null; 
      }).ToList(); 

     foreach (var propertyInfo in tProps) 
     { 
      var val = propertyInfo.GetValue(obj, null); 
      fooProps.First(e => e.Name == propertyInfo.Name).SetValue(this, val, null); 
     } 

     int id = (int)typeof(T).GetProperty(quxPropName).GetValue(obj, null); 
     Qux qux = quxs.Single(q => q.ID == id); 
     this.FooQux = string.Format("{0} - {1}", qux.ID, qux.Name); 
    } 

    public bool Selected { get; set; } 
    public int ID { get; set; } 
    public bool Active { get; set; } 
    public string FooQux { get; set; } 
} 

和通用擴展方法:

public static List<Foo<T>> ToFoo<T>(this IEnumerable<T> list, IEnumerable<Qux> quxs, string quxPropName) 
{ 
    List<Foo<T>> foos = null; 

    try 
    { 
     foos = list.Select(obj => new Foo<T>(obj, quxs, quxPropName)).ToList(); 
    } 
    catch 
    { 
     foos = new List<Foo<T>>(); 
    } 

    return foos; 
} 

與用法:

List<Foo<Bar>> bars = barCollection.ToFoo(quxCollection, "BarQux"); 
List<Foo<Baz>> bazs = bazCollection.ToFoo(quxCollection, "BazQux"); 
+1

添加一些異常處理,因爲這種方法也給誤操作的範圍,因爲'ToFoo'擴展方法可以用在其他實現'IEnumerable '的類上,你可能想要防止 – Sandesh