如何铸造范围<;T>;到IntRange

本文关键字:gt IntRange lt 何铸造 范围 | 更新日期: 2023-09-27 18:19:45

我使用的是本答案中描述的Range<T>类。

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

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函数。

如何铸造范围<;T>;到IntRange

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

首先,我会将原始的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);

这运行和工作都非常好。

使用构造函数而不是静态方法,这也允许您使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
    }
}

您需要更改创建方法以创建正确的范围:

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