【问题标题】:How to instantiate derived class from base class generically如何一般地从基类实例化派生类
【发布时间】:2013-05-20 03:04:44
【问题描述】:

我已经扩展了这篇文章中给出的单位转换器类: https://stackoverflow.com/a/7852721/1474894 这就是我现在拥有的:

public abstract class UnitBase<TUnitType, TValueType> where TUnitType : struct, IComparable, IConvertible, IFormattable
{
    protected static TUnitType BaseUnit;
    protected static TValueType BaseValue;

    private static ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>> ConversionsTo = new ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>>();

    private static ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>> ConversionsFrom = new ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>>();

    public static TValueType Convert(TValueType value, TUnitType from, TUnitType to)
    {
        // If both From/To are the same, don't do any work.
        if (from.Equals(to))
            return value;

        // Convert into the base unit, if required.
        var valueInBaseUnit = from.Equals(BaseUnit)
                                ? value
                                : ConversionsFrom[from](value);

        // Convert from the base unit into the requested unit, if required
        var valueInRequiredUnit = to.Equals(BaseUnit)
                                ? valueInBaseUnit
                                : ConversionsTo[to](valueInBaseUnit);

        return valueInRequiredUnit;
    }

    protected static void RegisterConversion(TUnitType convertToUnit, Func<TValueType, TValueType> conversionTo, Func<TValueType, TValueType> conversionFrom)
    {
        if (!ConversionsTo.TryAdd(convertToUnit, conversionTo))
            throw new ArgumentException("Already exists", "convertToUnit");
        if (!ConversionsFrom.TryAdd(convertToUnit, conversionFrom))
            throw new ArgumentException("Already exists", "convertToUnit");
    }

    public static string GetUnit(TUnitType unit)
    {
        var type = typeof(TUnitType);
        if (!type.IsEnum)
        {
            throw new ArgumentException();
        }

        return Enums.GetEnumDescription(unit as Enum);
    }

    public TValueType InUnit(TUnitType unit)
    {
        return Convert(BaseValue, BaseUnit, unit);
    }
}

我有这样的具体实现:

public enum TemperatureUnit
{
    [System.ComponentModel.Description("°C")]
    Celcius,

    [System.ComponentModel.Description("°F")]
    Fahrenheit
}

public class Temperature : UnitBase<TemperatureUnit, double>
{
    static Temperature()
    {
        BaseUnit = TemperatureUnit.Celcius;
        RegisterConversion(TemperatureUnit.Fahrenheit, x => x * 1.8d + 32d, x => (x - 32d) / 1.8d);
    }

    private Temperature(double value)
    {
        BaseValue = value;
    }

    public static Temperature FromUnit(double value, TemperatureUnit unit)
    {
        return new Temperature(Convert(value, unit, BaseUnit));
    }
}

我设法将 ToUnit 方法作为基类的一部分进行通用化,但是使用 FromUnit 方法,我现在每个具体类都有一个(它们看起来都非常相似)。我希望能够做到这一点:

Temperature temperature = Temperature.FromUnit(10, TemperatureUnit.Celcius);
Pressure pressure = Pressure.FromUnit(50, PressureUnit.Bar);

...无需在每个具体类中实现 FromUnit。因为它是一种静态方法,所以我不能通过使用接口来强制实现。我可以在具体类中有一个公共构造函数,并且有类似的东西:

public static T FromUnit<T>(TValueType value, TUnitType unit) where T : new()
{
    BaseValue = Convert(value, unit, BaseUnit);
    return new T();
}

但我想它不是那么好,我不得不去:

Temperature temperature = Temperature.FromUnit<Temperature>(10, TemperatureUnit.Celcius);

我认为&lt;Temperature&gt; 有点多余。

是否有可能以更好的方式实现泛型 FromUnit?

编辑: 基本上我想要,而不是在每个子类中实现 FromUnit:

public static Temperature FromUnit(double value, TemperatureUnit unit)
{
    return new Temperature(Convert(value, unit, BaseUnit));
}

public static Pressure FromUnit(double value, PressureUnit unit)
{
    return new Pressure(Convert(value, unit, BaseUnit));
}

public static Speed FromUnit(double value, SpeedUnit unit)
{
    return new Speed(Convert(value, unit, BaseUnit));
}

...

...将泛型 FromUnit 放入基类中。

【问题讨论】:

  • 您真的不应该在您的问题中包含 xml cmets,仅供参考。这会使您的问题变得比必要的更长。
  • 好的,我删除了 xml cmets。感谢您的提示。

标签: c# class oop design-patterns generics


【解决方案1】:

为什么不将 FromUnit 实现推送到 UnitBase 本身:

    public static TValueType FromUnit(TValueType value, TUnitType unit)
    {
        return Convert(value, BaseUnit, unit);
    }

【讨论】:

  • 这正是我想要做的,但是使用 FromUnit 我想返回具体类的实例(例如温度),而不是 TValueType(例如双精度)。
【解决方案2】:

您的“不太好的”解决方案中的&lt;Temperature&gt; 类型参数可能看起来是多余的,但实际上并非如此。当你写

Temperature temperature = Temperature.FromUnit<Temperature>(10, TemperatureUnit.Celcius);

代码实际上是翻译成

Temperature temperature = UnitBase<TemperatureUnit, double>.FromUnit<Temperature>(10, TemperatureUnit.Celcius);

编译器非常好,允许您使用Temperature 访问该方法。因为它实际上是在调用UnitBase.FromUnit(),所以它不知道你真的想使用Temperature,这就是为什么你必须提供它作为一个额外的类型参数。

【讨论】:

  • 好吧,额外的 &lt;Temperature&gt; 类型参数也许还不错,但我可能会坚持在每个子类中都实现 FromUnit,这样我就可以做到 Temperature temperature = Temperature.FromUnit(10, TemperatureUnit.Celcius); 并且我将能够保持每个子类的构造函数都是私有的。无论如何,谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-03-02
  • 2013-10-15
  • 1970-01-01
  • 2012-09-20
  • 2013-05-03
  • 2014-09-29
  • 2021-11-27
相关资源
最近更新 更多