繁体   English   中英

C# 类型安全克隆与 generics

[英]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>();

这是一个有效的调用,因为CarWindowVehiclePart的子类型。 尽管如此,很明显这个调用是荒谬的。


我该如何解决这个问题?

使用 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方法只有TGearstick Gearstick 如果T是,例如EngineWheel ,则转换将失败。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM