2015-10-28 20 views
1

我正在使用Range<T> class described in this answer如何将范围<T>投射到IntRange?

我创建了下面的子类,以创建一系列特定类型的:

public class IntRange : Range<Int32> 
{ 
    public static List<IntRange> ParseRanges(string ranges) 
    { 
     //... 
    } 
} 

为了完成ParseRanges功能,我需要投从Range<Int32>IntRange。这是因为我呼吁我加入Range<T>类的辅助功能:

//Create a range that automatically determines the min/max between two bounds. 
public static Range<T> Create(T bound1, T bound2) 
{ 
    if (bound1.CompareTo(bound2) <= 0) 
    { 
     return new Range<T>() { Minimum = bound1, Maximum = bound2 }; 
    } 
    else 
    { 
     return new Range<T>() { Minimum = bound2, Maximum = bound2 }; 
    } 
} 

的方法调用看起来如下:

IntRange range = (IntRange)Create(2, 0); 

首先,我明白,我不能合法地做这个演员,因为它可能为另一个人创建一个类:

public class VerySpecialIntRange : Range<Int32> { /* ... */ } 

编译器无法知道Range<Int32>应该是IntRange还是VerySpecialIntRange,因此当我尝试投射时它会吐出错误。

我该如何进行演员制作,以便我不需要为每个子类推出Create函数?当然,我有我自己的源代码,并且可以相对容易地做到这一点,但如果Range<T>是某个封闭的第三方库的一部分,并且我需要继承它,我不能轻易确定如何正确实现Create函数。

+0

为什么首先需要'IntRange'?只是为了包含静态方法'ParseRanges'?你有没有添加任何新的实例成员到'IntRange'? –

+1

而不是为每个派生的'Range'类创建'Create'方法,**不要创建派生的'Range'类。只处理'范围'。密封它甚至。在另一个类中创建一个静态方法,如果需要,可以使用'string'并返回'List >;但是除了像这样继承'Range'之外,你得到的只是麻烦。 – Servy

+1

噢,'Range'最好是不可变的,而不是可变的。如果你想保持不变,比如说最大值实际上更大,那就更加正确了。如果它是可变的,那么有人可以违反那个不变量。 – Servy

回答

0

使用构造函数,而不是一个静态方法,这也使您可以保护MinimumMaximum属性的制定者/私有的,因为它们真的应该是(应该是用户能够改变它们的值,最终在实例化之后重新定义对象?)。当默认逻辑足够时,将参数传递给基础构造函数,如果要覆盖派生类中的逻辑,请使用默认的构造函数base()

public class Range<T> 
{ 
    Range() { } 
    Range(T bound1, T bound2) { /* your static method logic here */ } 
} 

public class IntRange : Range<int> 
{ 
    IntRange(int bound1, int bound2) 
     : base(bound1, bound2) 
    { } 

    IntRange(int b1, int b2, bool someExampleFlag) 
    { 
     // custom logic here 
    } 
} 
+0

这是一个很好的解决方案。这样做有没有副作用?据我所知,这是正常工作。 –

0

你需要改变你的创建方法来创建正确的范围:

public class IntRange : Range<Int32> 
{ 
    public static List<IntRange> ParseRanges(string ranges) 
    { 
     return new[] { Create<IntRange, int>(1, 2) }.ToList(); 
    } 
} 

public static TRangeType Create<TRangeType, TItemType>(TItemType bound1, TItemType bound2) 
where TRangeType : Range<TItemType>, new() 
where TItemType : IComparable<TItemType> 
{ 
    if (bound1.CompareTo(bound2) <= 0) 
    { 
     return new TRangeType { Minimum = bound1 }; 
    } 
    else 
    { 
     return new TRangeType { Minimum = bound2, Maximum=bound2 }; 
    } 
} 
+0

当然现在你已经失去了推断'Create'类型的能力,使得使用起来非常麻烦。如果'Range'变得不可变(它真的应该),那么这就不再成为一个选项。 – Servy

0

这样做有点棘手,但您基本上需要创建一个抽象工厂模式。

首先,我会更改原始Range<T>类以具有签名public class Range<T> : Range where T : IComparable<T>

现在我定义了Range类就象这样:

public class Range 
{ 
    private static Dictionary<Type, Delegate> _factories = new Dictionary<Type, Delegate>(); 

    public static void AddFactory<T>(Func<T, T, Range<T>> factory) where T : IComparable<T> 
    { 
     _factories.Add(typeof(T), factory); 
    } 

    public static Range<T> Create<T>(T bound1, T bound2) where T : IComparable<T> 
    { 
     Func<T, T, Range<T>> factory = 
      _factories.ContainsKey(typeof(T)) 
       ? (Func<T, T, Range<T>>)_factories[typeof(T)] 
       : (Func<T, T, Range<T>>)((n, x) => new Range<T>() 
       { 
        Minimum = bound1, 
        Maximum = bound2 
       }); 

     return 
      bound1.CompareTo(bound2) <= 0 
       ? factory(bound1, bound2) 
       : factory(bound2, bound1); 
    } 
} 

这样做是创建一个Dictionary<Type, Delegate>,可容纳你需要创建所有你要定义的特殊范围类型的许多不同的工厂。当您致电Create<T>时,T的类型用于检查您是否有特殊工厂,如果是,则使用此工厂创建您的范围,否则使用默认的Range<T>类。

需要一个非通用的Range类来完成这项工作。如果您尝试将工厂代码放在Range<T>中,则会根据T获得单独的_factories实例。非泛型类只有一个。

您可以定义一个工厂是这样的:

Range.AddFactory<int>((n, x) => new IntRange() { Minimum = n, Maximum = x }); 

现在你可以打电话给你创造这样的代码:

IntRange range = (IntRange)Range.Create<int>(4, 9); 

这将运行和工作完全正常。

+0

我没有看到真正的区别与推出我自己的创建功能。我要求的是我必须继承的情况,但要依靠父母来创造。但在工厂方法中,我们告诉工厂逻辑必须是什么。如果我们试图依赖默认(父级)行为,我们将再次尝试将'Range '转换为'IntRange'。尽管如此,感谢您向我展示工厂的外观。 ^^ –

+1

@NickMiller - 这与将要创建的机制与要创建的机制分开的区别在于。它还允许您在运行时选择要返回的类,而无需更改'Range'类。如果没有你编写正确的代码,你希望你的代码能够区分'IntRange'和'VerySpecialIntRange'?这种方法甚至可以让你从不同的程序集中注入新的子类到代码中。这是一个运行时而不是编译时方法。我不认为其他方法会更接近你想要的。 – Enigmativity

相关问题