2017-08-17 58 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!