2013-05-22 47 views
3

我有一個基類Foo,其中包含一個用於標識數據庫項目中主鍵的屬性。 其他類從我的基類Foo下降,幷包含這些類的特殊性的另一個屬性。每個後代類表示數據庫中的一個不同的表。從基類型列表中返回正確的後代類型

通常,當這些數據被加載時,這些項目被存儲在類型列表<Foo>的相同列表中。

當我需要使用一些項目時,我發現這是基於主鍵。

我的問題發生在這些項目具有相同的主鍵,並且當從後代類FooB中查找項目時最終找到類FooA,因爲這是首先出現的,那麼執行到FooB的投射的位置會引起一個注意。

var list = new List<Foo>(); 

list.Add(new FooA() { PrimaryKey = 1 }); 
list.Add(nwe FooB() { PrimaryKey = 1 }); 

public Foo FindItem(int pk) 
{ 
    return list.First(it => it.PrimaryKey == 1); 
} 

在很多地方我用這種方法根據我的需要找到我的物品。

var item = (FooB)FindItem(1); 

在其他類:

var item = (FooA)FindItem(1); 

在我的情況下,該代碼在系統中的很多地方使用,更改列表<富>到下降的項目清單是一個解決方案,但這種手段在很多地方做出改變,因爲我無法在每個地方都傳遞每個項目的類型。

我需要一個不會更改基本列表的解決方案。

+0

爲什麼不能更改基準表? – Jonathan

回答

3

你需要重寫FindItem(1)方法返回沒有實例Foo而是一種特殊的對象,將包含與PrimaryKey==1和兩個FooA實例中的一個FooB實例與PrimaryKey==1

第二步是提供鑄造操作員將這個特殊對象轉換爲您需要的類型,以使代碼var item = (FooB)FindItem(1)按預期工作。

以下是實現此類解決方案的完整工作代碼。主要缺點是您必須爲您正在使用的每種派生類型提供投射運算符實現。你可能也可以從Foo中得出FooWrapper(在這種情況下,只有Foo中的一個屬性很簡單,但它取決於你的實際Foo類)。

using System; 
using System.Collections.Generic; 
using System.Linq; 

namespace FooWrapper 
{ 
    class Program 
    { 
     private static List<Foo> list = new List<Foo>(); 

     static FooWrapper FindItem(int id) 
     { 
      return new FooWrapper(list.Where(o => o.PrimaryKey == id)); 
     } 

     static void Main(string[] args) 
     { 
      list.Add(new FooA() { PrimaryKey = 1, Title = "aaa" }); 
      list.Add(new FooB() { PrimaryKey = 1, Age = 123 }); 

      // test the implicit cast to Foo type. this prefers the FooA type. 
      Foo item = FindItem(1); 
      Console.WriteLine(item.PrimaryKey + " - " + item.GetType().Name); 

      // retrieve the FooWrapper and cast it to FooA type. 
      var itema = (FooA)FindItem(1); 
      Console.WriteLine(itema.PrimaryKey + " - " + itema.Title + " - " + itema.GetType().Name); 

      // retrieve the FooWrapper and cast it to FooB type. The wrapper instance 
      // retrieved is the same, but the cast retrieves the correct type 
      var itemb = (FooB)FindItem(1); 
      Console.WriteLine(itemb.PrimaryKey + " - " + itemb.Age + " - " + itemb.GetType().Name); 
     } 
    } 

    public sealed class FooWrapper 
    { 
     private FooA _a; 
     private FooB _b; 

     public FooWrapper(IEnumerable<Foo> items) 
     { 
      FooA a; 
      FooB b; 
      foreach (var i in items) 
      { 
       a = i as FooA; 
       if (a != null) 
       { 
        this._a = a; 
       } 
       else 
       { 
        b = i as FooB; 
        if (b != null) 
         this._b = b; 
       } 
      } 
     } 

     public static implicit operator Foo(FooWrapper obj) 
     { 
      if (obj == null) 
       return null; 

      return obj._a == null ? (Foo)obj._b : obj._a; 
     } 

     public static explicit operator FooA(FooWrapper obj) 
     { 
      if (obj == null) 
       return null; 

      return obj._a; 
     } 

     public static explicit operator FooB(FooWrapper obj) 
     { 
      if (obj == null) 
       return null; 

      return obj._b; 
     } 
    } 

    public abstract class Foo 
    { 
     public int PrimaryKey { get; set; } 
    } 

    public class FooA : Foo 
    { 
     public string Title { get; set; } 
    } 

    public class FooB : Foo 
    { 
     public int Age { get; set; } 
    } 
} 
+0

+1,您的詳細代碼使我的第二個答案(基本上描述相同的想法)過時(只是刪除它)。但是,我會刪除隱式的Foo操作符,因爲OP提到他將結果存儲在'Foo'類型的變量中,並且稍後將其轉換爲'FooA'或'FooB'。 – Heinzi

+2

將它存儲在'Foo'類型的變量中的問題是,當您從'Foo'派生'FooWrapper'時,此代碼仍然失敗:'Foo i = FindItem(1); var ia =(FooA)i;'你必須寫'var ia =(FooA)(FooWrapper)ia;'這是不好的,因爲你現在必須知道什麼時候它是包裝器,什麼時候不是。 –

0
public T FindItem<T>(int pk) where T : Foo 
{ 
    return list.OfType<T>().First(it => it.PrimaryKey == 1); 
} 

用法:

var item = FindItem<FooB>(1); 

OfType<T>list兩個好處:

  • 它過濾列表項的那些T型和
  • 它返回一個IEnumerable<T>,所以FindItem的返回值已經是cor直接鍵入並且不需要被施放。
+0

該解決方案可以解決我的問題,但在大多數地方我將結果存儲在基類Foo或基類列表中,然後轉換爲特定類型。 – Richard

+0

@理查德:沒關係。由於'FooB'是'Foo'的子類型,因此您可以將'FindItem '的返回值存儲在'Foo'類型的變量中(或者列表「List '的類型列表中)。 – Heinzi

+0

好的,如果我使用泛型,它意味着在使用方法FindItem的地方改變。我無法在任何地方傳遞每種商品的類型 – Richard

0

使用泛型在你的FindItem功能:

public T FindItem<T> (int pk) where T : Foo 
{ 
    return list.OfType<T>().SingleOrDefault(it => it.PrimaryKey == pk); 
}