gpt4 book ai didi

c# - 在 C# : How to ensure methods in the base class return the derived class's type? 的基类中使用泛型

转载 作者:行者123 更新时间:2023-12-03 18:39:09 33 4
gpt4 key购买 nike

我正在为各种度量单位开发一个类库。我试图尽可能避免重复代码,这应该非常简单,因为从数学上讲,无论是将两个长度相加还是将两个速度相加都没有关系——您只需将它们转换为相同的单位,然后添加。我认为我可以使用基类和泛型很容易地做到这一点...

// A measurement consists of a Value (double) and
// a Unit (an Enum for the various unit types).

public class Measurement<TUnit>
where TUnit : struct
{
protected Measurement(double value, TUnit unit)
{
_value = value;
_unit = unit;
}

protected double _value;
protected TUnit _unit;

public double Value => _value;
public TUnit Unit => _unit;

...
// Conversion Methods - these will get overridden in the derived classes.
protected virtual double GetValueAs(TUnit unit) => throw new NotImplementedException();
...

// Operator overloads
public static Measurement<TUnit> operator +(Measurement<TUnit> left,
Measurement<TUnit> right)
{
return new Measurement<TUnit>(left.Value + right.GetValueAs(left.Unit), left.Unit);
}
}

并且像这样为每个单元派生此类:

public sealed class Length : Measurement<LengthUnit>
{
// Using a private constructor and public static factory methods
private Length(double value, LengthUnit unit)
: base(value, unit) { }

...
}

我的问题是每当我尝试使用任何返回 Measurement<TUnit> 的基类类型时,这些方法显然返回 Measurement<TUnit>对象。

Length length1 = Length.FromMeters(1); // length1 is 1 meter

Length length2 = Length.FromMeters(2); // length2 is 2 meters

Length length3 = length1 + length2; // Error CS0266: Cannot implicitly convert type
// 'Measurement<LengthUnit>' to 'Length'.
// An explicit conversion exists (are you missing a cast?)

var varlength3 = length1 + length2; // This works, but varlength3 is type Measurement<LengthUnit>
// so I can't use any methods in the Length class.

我错过了什么?甚至可以做我想做的事吗?我是否必须硬着头皮将相同的代码复制到每个单元类中?

最佳答案

简答:当您需要从基类操作派生类时,可以通过实现Curiously Recurring Template Pattern 来完成。 ,它最初来自 C++,但也可以应用于 C#。

长答案:在您的情况下,基础 Measurement 类应该有一个额外的通用参数,即派生类类型。唯一的限制是,如果派生类具有带参数的构造函数,则不能在基类中使用 new 创建派生实例。在下面的示例中,我使用 MemberwiseClone 可能会解决问题。

public class Measurement<TUnit, TMeasurement>
where TUnit : struct
where TMeasurement: Measurement<TUnit, TMeasurement>
{
private readonly double _value;
protected Measurement(double value, TUnit unit)
{
_value = value;
Unit = unit;
}
protected double Value { get; set; }
public TUnit Unit { get; protected set; }

// Conversion Methods - these will get overridden in the derived classes.
protected virtual double GetValueAs(TUnit unit) => throw new NotImplementedException();

// Operator overloads
public static TMeasurement operator +(Measurement<TUnit, TMeasurement> left, Measurement<TUnit, TMeasurement> right)
{
//we cannot create new instance of derived class, TMeasurement, which is limitation of generics in C#, so need some workaround there
//Some kind of clone might be solution for that
var leftClone = (TMeasurement)left.MemberwiseClone();
var resultValue = leftClone.Value + right.GetValueAs(left.Unit);
leftClone.Unit = left.Unit;
leftClone.Value = resultValue;
return leftClone;
}
}

public struct LengthUnit
{

}

public sealed class LengthMeasurement : Measurement<LengthUnit, LengthMeasurement>
{
private LengthMeasurement(double value, LengthUnit unit): base(value, unit)
{

}

public static LengthMeasurement FromMeters(double meters) => throw new NotImplementedException();
}

class Program
{
static void Main(string[] args)
{
var length1 = LengthMeasurement.FromMeters(5);
var length2 = LengthMeasurement.FromMeters(10);
LengthMeasurement length3 = length1 + length2;
}
}

关于c# - 在 C# : How to ensure methods in the base class return the derived class's type? 的基类中使用泛型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59546683/

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com