如何将Range <t>转换为IntRange?

时间:2015-10-28 22:46:27

标签: c# generics inheritance casting

我正在使用Range<T> class described in this answer

我创建了以下子类,以创建特定类型的范围:

if(isset($_POST['my_data'])){
      //process form
}

为了完成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>函数。

3 个答案:

答案 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 };
    }
}

答案 1 :(得分:0)

使用构造函数而不是静态方法,这也允许您使MinimumMaximum属性的setter保护/私有,因为它们确实应该是(如果用户能够更改)它们的值,最终在实例化后重新定义对象?)。当默认逻辑足够时,将参数传递给基础构造函数,如果要覆盖派生类中的逻辑,请使用默认的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
    }
}

答案 2 :(得分: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的类型用于检查您是否有特殊工厂,如果您这样做,它会使用此工厂来创建您的范围,否则它将使用默认值{{} 1}} class。

要使这项工作成为必需的非通用Range<T>课程。如果您尝试将工厂代码放在Range中,则每Range<T>个单独的_factories个实例。使用非泛型类,您只能得到一个。

您可以像这样定义工厂:

T

现在您可以像这样调用您的创建代码:

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

这样运行并完美无缺。