2017-08-17 60 views
7

我有一些類從一個抽象基類Airplane繼承,examplified:用類(或類似概念)的列表來限制有效輸入

Airplane 

-> F15 
-> F16 
-> Boeing747 

假設我要創建另一個類,AirplaneFactory那接受一個列表(構造函數)可能飛機就可以建立:

class AirplaneFactory { 
    public AirplaneFactory(List<Type> airplaneTypes) { 
     .... 
    } 
} 

如何限制這些類型僅Airplane和繼承的類?最終的目標是創造不同的AirplaneFactory實例作爲指定的,只能「建」飛機的特定子集。

我想它限制類本身,而無需通過使用enum或使用字符串表示的類重複的工作。

+0

見https://stackoverflow.com/questions/2742276/how-do-i-check-if:但是,這與重用工廠約束的方法來實現-a型是-A-亞型或最類型的-一個對象。我認爲這應該有你需要的信息。 – GBreen12

+0

儘管可能不是......我想這不會在技術上「限制」這些類型,它只是讓你檢查傳入的類型。 – GBreen12

+0

同樣的問題在這裏被問到https://stackoverflow.com/questions/8223073/define-list-of-specific-type-not-object,但是這個問題還沒有完全解答。 – 0liveradam8

回答

1

這裏有兩種可能的實現:

public class AirplaneFactory 
{ 
    private List<Type> _types = new List<Type>(); 

    //Implementation 1: Use an internal method to validate all items passed. 
    public AirplaneFactory(IEnumerable<Type> airplaneTypes) 
    { 
     AddTypes(airplaneTypes); 
    } 

    private void AddTypes(IEnumerable<Type> airplaneTypes) 
    { 
     var targetType = typeof(Airplane);    
     foreach (var item in airplaneTypes) 
     { 
      if (!item.IsSubclassOf(targetType)) 
       throw new ArgumentException(string.Format("{0} does not derive from {1}", item.FullName, targetType.FullName)); 
      _types.Add(targetType); 
     } 
    }   

    //Implementation 2: Use a method to individually add the supported types 
    public AirplaneFactory() 
    { 

    } 

    //This method adds types one by one and validates the type 
    public void AddType<T>() where T : Airplane 
    { 
     _types.Add(typeof(T)); 
    }    
} 

(注意使用IEnumerable<T>,而不是一個具體的列表)

測試它:

//Implementation 1: It will throw an error when FooClass is checked internally 
    var factory = new AirplaneFactory(new[] 
    { 
     typeof(F15), 
     typeof(F16), 
     typeof(Boeing747), 
     typeof(FooClass) 
    }); 

    //Implementation 2: 
    AirplaneFactory factory = new AirplaneFactory(); 
    factory.AddType<F15>(); 
    factory.AddType<F16>(); 
    factory.AddType<Boeing747>(); 
    //factory.AddType<FooClass>(); //This line would not compile. 

更新:

還有第三種可能的實現,如果你抽象出來的飛機類型集合的概念:

public class AirplaneTypeCollection : IEnumerable<Type> 
{ 
    List<Type> _types = new List<Type>(); 
    public AirplaneTypeCollection() 
    { 

    } 
    public void AddType<T>() where T: Airplane 
    { 
     _types.Add(typeof(T)); 
    } 

    public IEnumerator GetEnumerator() 
    { 
     return _types.GetEnumerator(); 
    } 

    IEnumerator<Type> IEnumerable<Type>.GetEnumerator() 
    { 
     return _types.GetEnumerator(); 
    } 
} 

然後你的工廠接收類作爲構造函數的參數:

public AirplaneFactory(AirplaneTypeCollection types) 
{ 

} 
+0

我目前的解決方案是這樣的,但「TypeCollection」是一個很好的接觸。我認爲沒有100%的方法可以做到這一點,但這可能是最重要的。 –

0

相反的類型列表中,你可以使用一個AddAirplaneType法的類型添加到您的工廠。

public void AddAirplaneType<TAirplane>() where TAirplane : Airplane 
{ 
    if(!airplaneTypes.Contains(typeof(TAirplane)) 
     airplaneTypes.Add(typeof(TAirplane)); 
} 

在編譯之前,這並不完美,但易於管理。將它作爲實例進行檢查後只能得到兩件事:

  1. 您必須以無提示方式跳過錯誤的類型。
  2. 你必須拋出異常。

我不喜歡這兩個,所以我會用一個Add-Method,也許是多個,一次支持多種類型。

AddAirplaneType<TAirplane1, TAirplane2, TAirplane3, TAiplane4>() 
    where TAirplane1 : Airplane 
    where TAirplane2 : Airplane 
    where TAirplane3 : Airplane 
    where TAirplane4 : Airplane 

你明白了。

0

這就是你如何做到的。使用仿製藥的工廠:

public abstract class Airplane 
{ 
} 

public class F15 : Airplane 
{ 
} 
public class F16 : Airplane 
{ 
} 
public class B747 : Airplane 
{ 
} 

public class AirplaneFactory<T> where T : Airplane, new() 
{ 
    public List<T> Inventory => new List<T>(); 

    public T Make() { return new T(); } 
} 

static class Program 
{ 
    static void Main(string[] args) 
    { 
     var b747_factory = new AirplaneFactory<B747>(); 
     var a_b747 = b747_factory.Make(); 
     b747_factory.Inventory.Add(a_b747); 
    } 
} 
0

如果你想要一個具有一組預定義的「原型」的類工廠,從中複製屬性,然後執行以下操作:

static class Program 
{ 
    static void Main(string[] args) 
    { 
     var b747_100 = new B747("100", false, 333400); 
     var b747_300 = new B747("800", false, 439985); 
     var b747_300sp = new B747("300sp", true, 287500); 

     var factory = new AirplaneFactory<B747>(b747_100, b747_300, b747_300sp); 

     var a_b747_300sp = factory.Make("300sp"); 
     // makes a cargo version of the B474 
     var a_b747_800 = factory.Make("800"); 
     // makes a passenger version of the B474 
     var a_b747_400 = factory.Make("400"); 
     // makes nothing. No prototype for 400 series 

     var copy_of_a_b747_800 = a_b747_800.Clone(); 
    } 
} 

/// <summary> 
/// Base class. Each airplane has a model moniker. 
/// </summary> 
public abstract class Airplane 
{ 
    protected Airplane(string model, int engineCount) 
    { 
     this.Model=model; 
     this.EngineCount=engineCount; 
    } 
    /// <summary> 
    /// Create a new airplane with properties from another (Copy constructor). 
    /// </summary> 
    /// <param name="other"></param> 
    protected Airplane(Airplane other) 
    { 
     this.Model=other.Model; 
     this.EngineCount=other.EngineCount; 
    } 
    public string Model { get; } 
    public int EngineCount { get; } 
    public abstract float Mass { get; } 
} 

public class F16 : Airplane, ICloneable 
{ 
    public F16(F16 prototype) : base(prototype) 
    { 
     this.Mass=prototype.Mass; 
    } 
    public F16(string model, float mass) : base(model, 1) 
    { 
     this.Mass=mass; 
    } 
    public override float Mass { get; } 

    #region ICloneable Members 
    public F16 Clone() { return new F16(this); } 
    object ICloneable.Clone() 
    { 
     return Clone(); 
    } 
    #endregion 

} 
public class B747 : Airplane, ICloneable 
{ 
    public B747(string model, bool isCargo, float mass) : base(model, 4) 
    { 
     this.IsCargo=isCargo; 
     this.Mass=mass; 
    } 
    public B747(B747 prototype) : base(prototype) 
    { 
     this.IsCargo=prototype.IsCargo; 
     this.Mass=prototype.Mass; 
    } 
    public bool IsCargo { get; } 
    public override float Mass { get; } 

    #region ICloneable Members 
    public B747 Clone() { return new B747(this); } 
    object ICloneable.Clone() 
    { 
     return Clone(); 
    } 
    #endregion 

} 

public class AirplaneFactory<T> where T : Airplane 
{ 
    /// <summary> 
    /// Use reflection to get the copy constructor for each type 'T' 
    /// </summary> 
    static ConstructorInfo ctor = typeof(T).GetConstructor(new[] { typeof(T) }); 
    /// <summary> 
    /// Hold a table of monikers and planes of type T 
    /// </summary> 
    readonly IDictionary<string, T> models; 
    /// <summary> 
    /// Create a factor with some built in plane models 
    /// </summary> 
    /// <param name="airplaneModels">The models that the factory can make</param> 
    public AirplaneFactory(params T[] airplaneModels) 
    { 
     this.models=airplaneModels.ToDictionary((x) => x.Model); 
    } 
    /// <summary> 
    /// Public API exposes a readonly table so users can't add whatever planes they want. 
    /// </summary> 
    public IReadOnlyDictionary<string, T> Models { get { return models as IReadOnlyDictionary<string, T>; } } 
    /// <summary> 
    /// Add a plan for new planes. If the model already exists then do nothing 
    /// </summary> 
    /// <param name="prototype">The plane prototype to implement</param> 
    /// <returns>True if the plans are added</returns> 
    public bool ImplementNewPlans(T prototype) 
    { 
     if(!models.ContainsKey(prototype.Model)) 
     { 
      models.Add(prototype.Model, prototype); 
      return true; 
     } 
     return false; 
    } 
    /// <summary> 
    /// Create a new plane from the prototype stored in 'Models'. 
    /// </summary> 
    /// <param name="model">The model moniker</param> 
    /// <returns>A new plane if the have a prototype, null otherwise</returns> 
    public T Make(string model) 
    { 
     if(models.ContainsKey(model)) 
     { 
      return ctor.Invoke(new object[] { models[model] }) as T; 
     } 
     return null; 
    } 
} 

備註:我公司採用通用的平面類型,構造和字典反射持有允許飛機模型

0

而不是通過Airplane類型的列表到AirplaneFactory構造函數,它傳遞的Func<Airplane>列表(創建飛機的代表)。

sealed class AirplaneFactory 
{ 
    public Airplane(IEnumerable<Func<Airplane>> airplane_types) 
    { 

    } 
} 

並使用像這樣:

//Can only make F15 and Boeing747 instances: 
new AirplaneFactory(List<Func<Airplane>> {() => new F15(),() => new Boeing747() }); 
1

爲了獲得最佳的編譯時安全,工廠類本身是可以通用的。通過限制泛型定義中的類型,程序總是可以假設只添加(或創建)了正確的類型。

雖然指定構造函數中的類型可以加快創建新的亞型,該檢查只能在運行時完成,但有例外

的幫助下通過繼承飛機本身,工廠可以調整到特定亞型。 例如具有以下設置:

public abstract class Airplane{} 
public abstract class Fighter:Airplane{} 

public class F15 : Fighter{} 
public class F16 : Fighter{} 
public class Boeing747 : Airplane{} 

public class AirplaneFactory<T> where T : Airplane 
{ 
    List<T> list = new List<T>(); 
    public void Add(T plane) => list.Add(plane); //"Add" used as a general example, but something like "Create" can be used as well. If T itself should be creatable directly, the constraint 'where T: Airplane, new()' can be used 
} 

下可以使用,最後一行給出一個編譯器錯誤:

var generalFact=new AirplaneFactory<Airplane>(); 
generalFact.Add(new F15()); //valid 
generalFact.Add(new Boeing747()); //valid 
var fighterFact = new AirplaneFactory<Fighter>(); 
fighterFact.Add(new F15()); //valid 
fighterFact.Add(new Boeing747()); //Invalid! 

因爲你可能需要更多的子類,然後繼承允許,您可以使用接口。

例如

public interface IAirplane{} 
public interface IFighter:IAirplane{} 
public interface IVertical:IAirplane{} 
public abstract class Airplane:IAirplane{} 

public class F15 : Airplane, IFighter{} 
public class F16 : Airplane, IFighter{} 
public class Boeing747 : Airplane{} 
public class F14: Airplane,IFighter,IVertical{} 

public class AirplaneFactory<T> where T : IAirplane 
{ 
    List<T> list = new List<T>(); 
    public void Add(T plane) => list.Add(plane); 
} 

並與接口使用工廠:

var generalFact=new AirplaneFactory<IAirplane>(); 
generalFact.Add(new F15()); //valid 
generalFact.Add(new Boeing747()); //valid 
var fighterFact = new AirplaneFactory<IFighter>(); 
fighterFact.Add(new F15()); //valid 
var verticalFact=new AirplaneFactory<IVertical>(); 
verticalFact.Add(new F14()); //valid 
verticalFact.Add(new F15()); //Invalid 

當然,因爲它是一個工廠,創建函數所預期的,而不是「添加」功能。但是對於一個通用的工廠,總是需要額外的規格。

public class AirplaneFactory<T> where T : IAirplane 
{ 
    List<T> list = new List<T>(); 
    public void Add(T plane) => list.Add(plane); 

    public PlaneType Create<PlaneType>() 
     where PlaneType:class,T,new() 
    { 
     var res = new PlaneType(); 
     Add(res); 
     return res; 
    } 
} 

例如

verticalFact.Create<F14>(); //valid 
verticalFact.Create<F15>(); //Invalid!