[英]C# typesafe cloning with generics
我正在尝试创建一个可以深度克隆的通用容器 object(完成;没问题)。 这意味着它必须深度克隆其包含的对象,而正是这给我带来了问题。 这是一些代码:
public abstract class VehiclePart {
public abstract T cloneTypesafe<T>()
where T : VehiclePart, new() // L1
}
public class Gearstick : VehiclePart { // L2
public Gearstick() { }
public override T cloneTypesafe<T>() {
var res2 = new Gearstick();
return res2; // L3
return (T)res2; // L4
}
}
它拒绝这个(评论 L3)说“不能隐式地将类型 'LDB.Program.Gearstick' 转换为 'T'”,但这没有任何意义,因为 T 显式派生自 VehiclePart(行 L1),而 Gearstick 显式派生自 VehiclePart (第 L2 行),所以转换应该是完全合法的。
即使我有一个明确的演员表(第 L4 行),它也不高兴(“无法将类型 'LDB.Program.Gearstick' 转换为'T'”),那么问题是什么,我该如何解决这个问题? (我不想使用反射,这似乎是回答类似问题的常见建议。我读过几条评论说不要使用 ICloneable)。
使用 C# 9 您可以在此处消除 generics 的需要并使用协变返回类型:
public abstract class VehiclePart
{
public abstract VehiclePart cloneTypesafe();
}
public class Gearstick : VehiclePart {
public Gearstick() { }
public override Gearstick cloneTypesafe()
{
return new Gearstick(); // your actual implementation
}
}
所以有什么问题
您的方法的签名允许我按如下方式调用您的代码:
CarWindow myCarWindow = myGearstick.cloneTypesafe<CarWindow>();
这是一个有效的调用,因为CarWindow
是VehiclePart
的子类型。 尽管如此,很明显这个调用是荒谬的。
我该如何解决这个问题?
使用 C# 9.0 或更高版本,您可以使用协变返回类型,请参阅Guru Stron 的回答。
如果您坚持使用早期版本,请参阅Jamiec 的答案以获取可能的解决方案。 或者,您可以将该方法提取到您自己的IDeepCloneable<T>
接口中以分离关注点:
public interface IDeepCloneable<T>
{
T CloneTypesafe();
}
public class Gearstick : VehiclePart, IDeepCloneable<Gearstick>
{
public Gearstick CloneTypesafe() { ... }
}
请注意,正如 Jamiec 在评论中指出的那样,类型系统不会阻止您进行无意义的声明,例如public class Gearstick: VehiclePart, IDeepClonable<Windscreen>
。
这是对 generics 的误用。 如果您使用 generics,您是说该方法的调用者可以传入他们想要的任何类型(有一些约束),并且您的方法仍然有效。 这显然不是这里的情况。 Gearstick 中的cloneTypesafe
方法只有在T
是Gearstick
Gearstick
。 如果T
是,例如Engine
或Wheel
,则转换将失败。
cloneTypesafe
应该真正返回“它所在的 class 的类型”,但我们无法在 C# 中这么说。 不过,您可以将其作为一种解决方法:
public abstract class VehiclePart<T>
where T : VehiclePart<T>, new(){ // implementers are supposed to replace T with their class
public abstract T cloneTypesafe();
}
// you are not "forced" to say ": VehiclePart<Gearstick>", hence why this is only a workaround
public class Gearstick : VehiclePart<Gearstick> {
public override Gearstick cloneTypesafe() {
var res2 = new Gearstick();
// copy some properties...
return res2;
}
}
为了使它像(我认为)您所期望的那样工作,您应该对抽象 class 本身具有通用定义
public abstract class VehiclePart<T> where T: new() {
public abstract T cloneTypesafe();
}
public class Gearstick : VehiclePart<Gearstick> { // L2
public Gearstick() { }
public override Gearstick cloneTypesafe() {
var res2 = new Gearstick();
return res2;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.