为什么没有 ICloneable

发布于 2024-07-13 07:45:27 字数 91 浏览 8 评论 0原文

通用 ICloneable 不存在是否有特殊原因?

如果我不需要每次克隆某些东西时都投射它,那就会舒服得多。

Is there a particular reason why a generic ICloneable<T> does not exist?

It would be much more comfortable, if I would not need to cast it everytime I clone something.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

海之角 2024-07-20 07:45:27

除了 Andrey 的回复(我同意,+1) - 当 ICloneable 完成时,您还可以选择显式实现来使公共 Clone() 返回类型化对象:

public Foo Clone() { /* your code */ }
object ICloneable.Clone() {return Clone();}

当然,通用 ICloneable 还存在第二个问题 - 继承。

如果我:

public class Foo {}
public class Bar : Foo {}

并且我实现了 ICloneable,那么我是否实现 ICloneableICloneable? 您很快就开始实现许多相同的接口......
与演员相比……真的有那么糟糕吗?

In addition to Andrey's reply (which I agree with, +1) - when ICloneable is done, you can also choose explicit implementation to make the public Clone() return a typed object:

public Foo Clone() { /* your code */ }
object ICloneable.Clone() {return Clone();}

Of course there is a second issue with a generic ICloneable<T> - inheritance.

If I have:

public class Foo {}
public class Bar : Foo {}

And I implemented ICloneable<T>, then do I implement ICloneable<Foo>? ICloneable<Bar>? You quickly start implementing a lot of identical interfaces...
Compare to a cast... and is it really so bad?

不喜欢何必死缠烂打 2024-07-20 07:45:27

ICloneable 现在被认为是一个糟糕的 API,因为它没有指定结果是深拷贝还是浅拷贝。 我认为这就是他们不改进这个界面的原因。

您可能可以执行类型化克隆扩展方法,但我认为它需要不同的名称,因为扩展方法的优先级低于原始方法。

ICloneable is considered a bad API now, since it does not specify whether the result is a deep or a shallow copy. I think this is why they do not improve this interface.

You can probably do a typed cloning extension method, but I think it would require a different name since extension methods have less priority than original ones.

混浊又暗下来 2024-07-20 07:45:27

我想问,除了实现接口之外,您究竟会用该接口做什么? 接口通常仅在您强制转换时才有用(即此类是否支持“IBar”),或者具有接受它的参数或设置器(即我接受“IBar”)。 使用 ICloneable - 我们浏览了整个框架,但没有找到除了它的实现之外的任何单一用法。 我们也未能在“现实世界”中找到除了实现它之外的任何用途(在我们可以访问的约 60,000 个应用程序中)。

现在,如果您只想强制执行您希望“可克隆”对象实现的模式,那么这是一个完全好的用法 - 然后继续。 您还可以决定“克隆”对您到底意味着什么(即深克隆或浅克隆)。 然而,在这种情况下,我们(BCL)就没有必要定义它。 仅当需要在不相关的库之间交换类型为抽象的实例时,我们才在 BCL 中定义抽象。

大卫·基恩(BCL 队)

I need to ask, what exactly would you do with the interface other than implement it? Interfaces are typically only useful when you cast to it (ie does this class support 'IBar'), or have parameters or setters that take it (ie i take an 'IBar'). With ICloneable - we went through the entire Framework and failed to find a single usage anywhere that was something other than an implementation of it. We've also failed to find any usage in the 'real world' that also does something other than implement it (in the ~60,000 apps that we have access to).

Now if you would just like to enforce a pattern that you want your 'cloneable' objects to implement, that's a completely fine usage - and go ahead. You can also decide on exactly what "cloning" means to you (ie deep or shallow). However, in that case, there's no need for us (the BCL) to define it. We only define abstractions in the BCL when there is a need to exchange instances typed as that abstraction between unrelated libraries.

David Kean (BCL Team)

一紙繁鸢 2024-07-20 07:45:27

我认为“为什么”这个问题是没有必要的。 有很多接口/类/等等...非常有用,但不是 .NET Frameworku 基础库的一部分。

但是,主要是你可以自己做。

public interface ICloneable<T> : ICloneable {
    new T Clone();
}

public abstract class CloneableBase<T> : ICloneable<T> where T : CloneableBase<T> {
    public abstract T Clone();
    object ICloneable.Clone() => return this.Clone();
}

public abstract class CloneableExBase<T> : CloneableBase<T> where T : CloneableExBase<T> {
    protected abstract T CreateClone();
    protected abstract void FillClone(T clone);
    public override T Clone() {
        T clone = this.CreateClone();
        if (clone is null ) {
            throw new NullReferenceException( "Clone was not created." );
        }

        this.FillClone(clone);
        return clone
    }
}

public abstract class PersonBase<T> : CloneableExBase<T> where T : PersonBase<T> {
    public string Name { get; set; }

    protected override void FillClone( T clone ) {
        clone.Name = this.Name;
    }
}

public sealed class Person : PersonBase<Person> {
    protected override Person CreateClone() => return new Person();
}

public abstract class EmployeeBase<T> : PersonBase<T> where T : EmployeeBase<T> {
    public string Department { get; set; }

    protected override void FillClone(T clone) {
        base.FillClone(clone);

        clone.Department = this.Department;
    }
}

public sealed class Employee : EmployeeBase<Employee> {
    protected override Employee CreateClone() => return new Employee();
}

I think the question "why" is needless. There is a lot of interfaces/classes/etc... which is very usefull, but is not part of .NET Frameworku base library.

But, mainly you can do it yourself.

public interface ICloneable<T> : ICloneable {
    new T Clone();
}

public abstract class CloneableBase<T> : ICloneable<T> where T : CloneableBase<T> {
    public abstract T Clone();
    object ICloneable.Clone() => return this.Clone();
}

public abstract class CloneableExBase<T> : CloneableBase<T> where T : CloneableExBase<T> {
    protected abstract T CreateClone();
    protected abstract void FillClone(T clone);
    public override T Clone() {
        T clone = this.CreateClone();
        if (clone is null ) {
            throw new NullReferenceException( "Clone was not created." );
        }

        this.FillClone(clone);
        return clone
    }
}

public abstract class PersonBase<T> : CloneableExBase<T> where T : PersonBase<T> {
    public string Name { get; set; }

    protected override void FillClone( T clone ) {
        clone.Name = this.Name;
    }
}

public sealed class Person : PersonBase<Person> {
    protected override Person CreateClone() => return new Person();
}

public abstract class EmployeeBase<T> : PersonBase<T> where T : EmployeeBase<T> {
    public string Department { get; set; }

    protected override void FillClone(T clone) {
        base.FillClone(clone);

        clone.Department = this.Department;
    }
}

public sealed class Employee : EmployeeBase<Employee> {
    protected override Employee CreateClone() => return new Employee();
}
秋意浓 2024-07-20 07:45:27

自己编写界面非常容易< /a> 如果你需要它:

public interface ICloneable<T> : ICloneable
        where T : ICloneable<T>
{
    new T Clone();
}

It's pretty easy to write the interface yourself if you need it:

public interface ICloneable<T> : ICloneable
        where T : ICloneable<T>
{
    new T Clone();
}
我爱人 2024-07-20 07:45:27

最近阅读了文章 为什么复制对象是一件可怕的事情?,我认为这个问题需要进一步澄清。 这里的其他答案提供了很好的建议,但答案仍然不完整 - 为什么没有 ICloneable

  1. 使用

    所以,你有一个实现它的类。 虽然以前您有一个需要 ICloneable 的方法,但现在它必须是通用的才能接受 ICloneable。 您需要对其进行编辑。

    然后,您可能有一个方法来检查对象是否是 ICloneable。 现在怎么办? 您不能执行 is ICloneable<> 并且由于您不知道编译类型时对象的类型,因此无法使该方法通用。 第一个真正的问题。

    因此您需要同时拥有 ICloneableICloneable,前者实现后者。 因此,实现者需要实现这两种方法 - object Clone()T Clone()。 不,谢谢,我们已经从 IEnumerable 中获得了足够的乐趣。

    正如已经指出的,继承也很复杂。 虽然协变似乎可以解决这个问题,但派生类型需要实现其自己类型的 ICloneable,但已经有一个具有相同签名(= 参数,基本上)的方法 - <基类的 code>Clone() 。 使新的克隆方法接口显式化是没有意义的,您将失去创建 ICloneable 时所寻求的优势。 因此添加 new 关键字。 但不要忘记,您还需要重写基类的 Clone() (所有派生类的实现必须保持统一,即从每个克隆方法返回相同的对象,因此基本克隆方法必须是虚拟的)! 但不幸的是,您不能同时使用相同的签名来overridenew 方法。 选择第一个关键字,您将失去添加 ICloneable 时想要达到的目标。 选择第二个,您将破坏接口本身,使应该执行相同操作的方法返回不同的对象。

  2. 你想要 ICloneable 来获得舒适感,但舒适感并不是接口设计的目的,它们的含义是(在一般 OOP 中)统一对象的行为(尽管在 C# 中,它是仅限于统一外部行为,例如方法和属性,而不是它们的工作原理)。

    如果第一个原因还没有说服您,您可以反对 ICloneable 也可以限制性地工作,以限制从克隆方法返回的类型。 然而,讨厌的程序员可以实现 ICloneable,其中 T 不是实现它的类型。 因此,为了实现您的限制,您可以向通用参数添加一个很好的约束:
    公共接口 ICloneable; : ICloneable 其中 T : ICloneable
    当然比没有 where 的限制更严格,您仍然不能限制 T 是实现接口的类型(您可以从 ICloneable派生; 实现它的不同类型)。

    你看,连这个目的都无法实现(原来的ICloneable也失败了,没有接口能够真正限制实现类的行为)。

正如您所看到的,这证明通用接口既难以完全实现,又确实不需要且无用。

但回到问题上来,你真正寻求的是克隆对象时的舒适感。 有两种方法可以做到这一点:

其他方法

public class Base : ICloneable
{
    public Base Clone()
    {
        return this.CloneImpl() as Base;
    }

    object ICloneable.Clone()
    {
        return this.CloneImpl();
    }

    protected virtual object CloneImpl()
    {
        return new Base();
    }
}

public class Derived : Base
{
    public new Derived Clone()
    {
        return this.CloneImpl() as Derived;
    }

    protected override object CloneImpl()
    {
        return new Derived();
    }
}

该解决方案为用户提供了舒适度和预期行为,但实施起来也太长了。 如果我们不想让“舒适”的方法返回当前类型,那么使用公共虚拟对象 Clone() 会容易得多。

那么让我们看看“最终”解决方案 - C# 中的什么真正目的是为了给我们带来安慰?

扩展方法!

public class Base : ICloneable
{
    public virtual object Clone()
    {
        return new Base();
    }
}

public class Derived : Base
{
    public override object Clone()
    {
        return new Derived();
    }
}

public static T Copy<T>(this T obj) where T : class, ICloneable
{
    return obj.Clone() as T;
}

它被命名为Copy,以免与当前的Clone方法发生冲突(编译器更喜欢类型自己声明的方法而不是扩展方法)。 class 约束是为了速度(不需要空检查等)。

我希望这能澄清为什么不使 ICloneable 的原因。 但是,建议根本不要实现ICloneable

Having read recently the article Why Copying an Object is a terrible thing to do?, I think this question needs additional clafirication. Other answers here provide good advices, but still the answer isn't complete - why no ICloneable<T>?

  1. Usage

    So, you have a class that implements it. While previously you had a method that wanted ICloneable, it now has to be generic to accept ICloneable<T>. You would need to edit it.

    Then, you could have got a method that checks if an object is ICloneable. What now? You can't do is ICloneable<> and as you don't know the type of the object at compile-type, you can't make the method generic. First real problem.

    So you need to have both ICloneable<T> and ICloneable, the former implementing the latter. Thus an implementer would need to implement both methods - object Clone() and T Clone(). No, thanks, we already have enough fun with IEnumerable.

    As already pointed out, there is also the complexity of inheritance. While covariance may seem to solve this problem, a derived type needs to implement ICloneable<T> of its own type, but there is already a method with the same signature (= parameters, basically) - the Clone() of the base class. Making your new clone method interface explicit is pointless, you will lose the advantage you sought when creating ICloneable<T>. So add the new keyword. But don't forget that you would also need to override the base class' Clone() (the implementation has to remain uniform for all derived classes, i.e. to return the same object from every clone method, so the base clone method has to be virtual)! But, unfortunately, you can't both override and new methods with the same signature. Choosing the first keyword, you'd lose the goal you wanted to have when adding ICloneable<T>. Chossing the second one, you'd break the interface itself, making methods that should do the same return different objects.

  2. Point

    You want ICloneable<T> for comfort, but comfort is not what interfaces are designed for, their meaning is (in general OOP) to unify the behavior of objects (although in C#, it is limited to unifying the outer behavior, e.g. the methods and properties, not their workings).

    If the first reason hasn't convinced you yet, you could object that ICloneable<T> could also work restrictively, to limit the type returned from the clone method. However, nasty programmer can implement ICloneable<T> where T is not the type that is implementing it. So, to achieve your restriction, you can add a nice constraint to the generic parameter:
    public interface ICloneable<T> : ICloneable where T : ICloneable<T>
    Certainly more restrictive that the one without where, you still can't restrict that T is the type that is implementing the interface (you can derive from ICloneable<T> of different type that implements it).

    You see, even this purpose couldn't be achieved (the original ICloneable also fails at this, no interface can truly limit the behavior of the implementing class).

As you can see, this proves making the generic interface is both hard to fully implement and also really unneeded and useless.

But back to the question, what you really seek is to have comfort when cloning an object. There are two ways to do it:

Additional methods

public class Base : ICloneable
{
    public Base Clone()
    {
        return this.CloneImpl() as Base;
    }

    object ICloneable.Clone()
    {
        return this.CloneImpl();
    }

    protected virtual object CloneImpl()
    {
        return new Base();
    }
}

public class Derived : Base
{
    public new Derived Clone()
    {
        return this.CloneImpl() as Derived;
    }

    protected override object CloneImpl()
    {
        return new Derived();
    }
}

This solution provides both comfort and intended behavior to users, but it's also too long to implement. If we didn't want to have the "comfortable" method returning the current type, it is much more easy to have just public virtual object Clone().

So let's see the "ultimate" solution - what in C# is really intented to give us comfort?

Extension methods!

public class Base : ICloneable
{
    public virtual object Clone()
    {
        return new Base();
    }
}

public class Derived : Base
{
    public override object Clone()
    {
        return new Derived();
    }
}

public static T Copy<T>(this T obj) where T : class, ICloneable
{
    return obj.Clone() as T;
}

It's named Copy not to collide with the current Clone methods (compiler prefers the type's own declared methods over extension ones). The class constraint is there for speed (doesn't require null check etc.).

I hope this clarifies the reason why not to make ICloneable<T>. However, it is recommended not to implement ICloneable at all.

恋竹姑娘 2024-07-20 07:45:27

一个大问题是他们不能限制T是同一类。 例如,什么会阻止您这样做:

interface IClonable<T>
{
    T Clone();
}

class Dog : IClonable<JackRabbit>
{
    //not what you would expect, but possible
    JackRabbit Clone()
    {
        return new JackRabbit();
    }

}

它们需要一个参数限制,例如:

interfact IClonable<T> where T : implementing_type

A big problem is that they could not restrict T to be the same class. Fore example what would prevent you from doing this:

interface IClonable<T>
{
    T Clone();
}

class Dog : IClonable<JackRabbit>
{
    //not what you would expect, but possible
    JackRabbit Clone()
    {
        return new JackRabbit();
    }

}

They need a parameter restriction like:

interfact IClonable<T> where T : implementing_type
不离久伴 2024-07-20 07:45:27

这是一个非常好的问题...不过,您可以自己提出:

interface ICloneable<T> : ICloneable
{
  new T Clone ( );
}

Andrey 说它被认为是一个糟糕的 API,但我还没有听说过有关此接口被弃用的任何消息。 这会破坏大量的接口......
Clone 方法应该执行浅复制。
如果对象还提供深复制,则可以使用重载的 Clone ( bool deep )。

编辑:我用于“克隆”对象的模式是在构造函数中传递原型。

class C
{
  public C ( C prototype )
  {
    ...
  }
}

这消除了任何潜在的冗余代码实现情况。
顺便说一句,谈论 ICloneable 的局限性,难道不是由对象本身来决定是否应该执行浅克隆或深克隆,甚至是部分浅/部分深克隆吗? 只要该对象按预期工作,我们真的应该关心吗? 在某些情况下,一个好的克隆实现很可能包括浅克隆和深克隆。

It's a very good question... You could make your own, though:

interface ICloneable<T> : ICloneable
{
  new T Clone ( );
}

Andrey says it's considered a bad API, but i have not heard anything about this interface becoming deprecated. And that would break tons of interfaces...
The Clone method should perform a shallow copy.
If the object also provides deep copy, an overloaded Clone ( bool deep ) can be used.

EDIT: Pattern i use for "cloning" an object, is passing a prototype in the constructor.

class C
{
  public C ( C prototype )
  {
    ...
  }
}

This removes any potential redundant code implementation situations.
BTW, talking about the limitations of ICloneable, isn't it really up to the object itself to decide whether a shallow clone or deep clone, or even a partly shallow/partly deep clone, should be performed? Should we really care, as long as the object works as intended? In some occasions, a good Clone implementation might very well include both shallow and deep cloning.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文