implement ejemplo c# .net icloneable

c# - ejemplo - ¿Por qué no ICloneable<T>?



icloneable c# ejemplo (9)

¿Hay alguna razón particular por la que no existe un ICloneable<T> genérico?

Sería mucho más cómodo si no tuviera que lanzarlo cada vez que clonara algo.


Además de la respuesta de Andrey (con la que estoy de acuerdo, +1): cuando se realiza ICloneable , también puede elegir la implementación explícita para hacer que el Clone() devuelva un objeto escrito:

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

Por supuesto, hay un segundo problema con una ICloneable<T> genérica de ICloneable<T> .

Si tengo:

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

E implementé ICloneable<T> , ¿entonces implemento ICloneable<Foo> ? ICloneable<Bar> ? Rápidamente empiezas a implementar muchas interfaces idénticas ... Compara con un reparto ... ¿es realmente tan malo?


Aunque la pregunta es muy antigua (5 años después de escribir estas respuestas :) y ya fue respondida, pero encontré que este artículo responde bastante bien a la pregunta, verifique here

EDITAR:

Aquí está la cita del artículo que responde a la pregunta (asegúrese de leer el artículo completo, incluye otras cosas interesantes):

Hay muchas referencias en Internet que apuntan a una publicación en el blog de 2003 de Brad Abrams, en la época en que trabajaba en Microsoft, en la que se discuten algunos pensamientos acerca de IClonable. La entrada del blog se puede encontrar en esta dirección: Implementación de ICloneable . A pesar del título engañoso, esta entrada de blog requiere no implementar ICloneable, principalmente debido a la confusión superficial / profunda. El artículo termina con una sugerencia directa: si necesita un mecanismo de clonación, defina su propia metodología de clonación o copia y asegúrese de documentar claramente si se trata de una copia profunda o superficial. Un patrón apropiado es: public <type> Copy();


Creo que la pregunta "por qué" es innecesaria. Hay muchas interfaces / clases / etc ... que son muy útiles, pero no forman parte de la biblioteca base de .NET Frameworku.

Pero, principalmente puedes hacerlo tú mismo.

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 ( object.ReferenceEquals( clone, null ) ) { throw new NullReferenceException( "Clone was not created." ); } 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(); } }



Es una muy buena pregunta ... Podrías hacer la tuya, sin embargo:

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

Andrey dice que se considera una API mala, pero no he escuchado nada acerca de que esta interfaz esté en desuso. Y eso rompería toneladas de interfaces ... El método Clone debería realizar una copia superficial. Si el objeto también proporciona una copia en profundidad, se puede usar un Clon sobrecargado (bool deep).

EDITAR: El patrón que uso para "clonar" un objeto, está pasando un prototipo en el constructor.

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

Esto elimina cualquier situación de implementación de código redundante potencial. Por cierto, hablando de las limitaciones de ICloneable, ¿no es realmente el objetivo en sí decidir si se debe realizar un clon superficial o un clon profundo, o incluso un clon en parte superficial / parcialmente profundo? ¿Deberíamos realmente importarnos, siempre y cuando el objeto funcione según lo previsto? En algunas ocasiones, una buena implementación Clon podría incluir una clonación tanto superficial como profunda.


Habiendo leído recientemente el artículo ¿Por qué copiar un objeto es algo terrible? , Creo que esta pregunta necesita una clarificación adicional. Otras respuestas aquí brindan buenos consejos, pero aún así la respuesta no es completa. ¿Por qué no hay ICloneable<T> ?

  1. Uso

    Entonces, tienes una clase que lo implementa. Mientras que anteriormente tenía un método que quería ICloneable , ahora tiene que ser genérico para aceptar ICloneable<T> . Necesitarías editarlo.

    Entonces, podrías tener un método que verifica si un objeto is ICloneable . ¿Ahora que? No puedes hacer is ICloneable<> y como no sabes el tipo de objeto en compile-type, no puedes hacer que el método sea genérico. Primer problema real.

    Por lo tanto, debe tener ICloneable<T> e ICloneable , el primero implementando el segundo. Por lo tanto, un implementador necesitaría implementar ambos métodos: el object Clone() y T Clone() . No, gracias, ya nos divertimos lo suficiente con IEnumerable .

    Como ya se señaló, también existe la complejidad de la herencia. Si bien la covarianza puede parecer resolver este problema, un tipo derivado necesita implementar ICloneable<T> de su propio tipo, pero ya existe un método con la misma firma (= parámetros, básicamente): el Clone() de la clase base. Hacer que su nueva interfaz de método de clonación sea explícita no tiene sentido, perderá la ventaja que buscó al crear ICloneable<T> . Así que agregue la new palabra clave. Pero no olvide que también debería reemplazar la clase base '' Clone() (la implementación debe permanecer uniforme para todas las clases derivadas, es decir, devolver el mismo objeto de cada método de clonación, por lo que el método de clonación de base debe ser virtual )! Pero, desafortunadamente, no se pueden override y los new métodos con la misma firma. Al elegir la primera palabra clave, perdería el objetivo que deseaba al agregar ICloneable<T> . Al elegir el segundo, se rompería la interfaz, haciendo que los métodos que deberían hacer lo mismo devuelvan objetos diferentes.

  2. Punto

    Desea ICloneable<T> para mayor comodidad, pero la comodidad no es para lo que se diseñan las interfaces, su significado es (en general, OOP) unificar el comportamiento de los objetos (aunque en C #, se limita a unificar el comportamiento externo, por ejemplo, los métodos y propiedades, no su funcionamiento).

    Si la primera razón aún no lo ha convencido, podría objetar que ICloneable<T> también podría funcionar de forma restrictiva, para limitar el tipo devuelto por el método de clonación. Sin embargo, un programador desagradable puede implementar ICloneable<T> donde T no es el tipo que lo está implementando. Entonces, para lograr su restricción, puede agregar una restricción agradable al parámetro genérico:
    public interface ICloneable<T> : ICloneable where T : ICloneable<T>
    Ciertamente, más restrictivo que el que no tiene where , todavía no puede restringir que T es el tipo que implementa la interfaz (puede derivar de ICloneable<T> de diferente tipo que lo implementa).

    Verá, incluso este propósito no se pudo lograr (el ICloneable original también falla en esto, ninguna interfaz puede realmente limitar el comportamiento de la clase implementadora).

Como puede ver, esto demuestra que hacer que la interfaz genérica sea difícil de implementar por completo y también es realmente innecesario e inútil.

Pero volviendo a la pregunta, lo que realmente busca es tener comodidad al clonar un objeto. Hay dos maneras de hacerlo:

Métodos adicionales

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(); } }

Esta solución proporciona comodidad y comportamiento deseado a los usuarios, pero también es demasiado largo para implementar. Si no quisiéramos que el método "cómodo" devolviera el tipo actual, es mucho más fácil tener solo public virtual object Clone() .

Así que veamos la solución "definitiva": ¿qué es lo que en C # está realmente destinado a brindarnos comodidad?

Métodos de extensión!

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; }

Se llama Copiar para que no coincida con los métodos de clonación actuales (el compilador prefiere los métodos declarados propios del tipo en lugar de los de extensión). La restricción de class está ahí para la velocidad (no requiere verificación nula, etc.).

Espero que esto aclare la razón de por qué no hacer ICloneable<T> . Sin embargo, se recomienda no implementar ICloneable en absoluto.


ICloneable ahora se considera una API mala, ya que no especifica si el resultado es una copia profunda o superficial. Creo que es por eso que no mejoran esta interfaz.

Probablemente pueda hacer un método de extensión de clonación con tipo, pero creo que requeriría un nombre diferente, ya que los métodos de extensión tienen menos prioridad que los originales.


Necesito preguntar, ¿qué haría exactamente con la interfaz además de implementarla? Las interfaces suelen ser útiles solo cuando se realizan conversiones (es decir, ¿admite esta clase ''IBar''), o tienen parámetros o configuradores que lo toman (es decir, tomo una ''IBar'')? Con ICloneable, pasamos por todo el Framework y no pudimos encontrar un solo uso en cualquier lugar que fuera algo diferente a su implementación. Tampoco hemos podido encontrar ningún uso en el "mundo real" que también haga algo más que implementarlo (en las ~ 60,000 aplicaciones a las que tenemos acceso).

Ahora, si solo desea imponer un patrón que desea que implementen sus objetos ''clonables'', ese es un uso completamente correcto y siga adelante. También puede decidir exactamente qué significa para usted la "clonación" (es decir, profunda o superficial). Sin embargo, en ese caso, no es necesario que nosotros (el BCL) lo definamos. Solo definimos abstracciones en el BCL cuando es necesario intercambiar instancias escritas como esa abstracción entre bibliotecas no relacionadas.

David Kean (Equipo BCL)


Un gran problema es que no pudieron restringir T para que sea la misma clase. Por ejemplo, lo que le impediría hacer esto:

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

Necesitan una restricción de parámetros como:

interfact IClonable<T> where T : implementing_type