Pregunta

¿Hay alguna razón en particular por un ICloneable<T> genérica no existe?

Sería mucho más cómodo, si yo no iba a necesitar para su emisión cada vez que clonar algo.

¿Fue útil?

Solución

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

Puede hacer probablemente un método de extensión clonación escrito, pero creo que requeriría un nombre diferente, ya que los métodos de extensión tienen menos prioridad que las originales.

Otros consejos

Además de la respuesta de Andrey (que estoy de acuerdo, 1) - cuando ICloneable es hecho, también puede optar por la aplicación explícita para que el Clone() pública devolver un objeto escrito:

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

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

Si tengo:

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

Y he implementado ICloneable<T>, entonces puedo implementar ICloneable<Foo>? ICloneable<Bar>? Rápidamente empezar a aplicar una gran cantidad de interfaces idénticas ... Comparar a un elenco ... y es realmente tan malo?

Tengo que preguntar, ¿qué haría usted con la interfaz que no sea implementarla? Las interfaces son por lo general sólo es útil cuando lanzas a ella (es decir, son compatibles con esta clase 'IBar'), o tienen parámetros o los emisores que tomaron (es decir, tomo un 'IBar'). Con ICloneable - fuimos a través de todo el marco y no pudo encontrar un solo uso en cualquier lugar que era algo más que una implementación de la misma. También hemos fracasado en encontrar algún uso en el 'mundo real' que también hace algo más que ponerlo en práctica (en los ~ 60.000 aplicaciones que tenemos acceso a).

Ahora si usted acaba de querer imponer un patrón que desea que los objetos '' Cloneable para poner en práctica, eso es un uso completamente bien - y seguir adelante. También puede decidir exactamente lo que la "clonación" significa para usted (es decir, profunda o superficial). Sin embargo, en ese caso, no hay necesidad para nosotros (BCL) para definirla. Sólo nos definimos abstracciones en el BCL cuando hay una necesidad de intercambiar casos mecanografiadas como la abstracción entre bibliotecas no relacionados.

David Kean (BCL Team)

Creo que la pregunta "por qué" es innecesaria. Hay una gran cantidad de interfaces / clases / etc ... lo cual es muy útil, pero no es parte de la biblioteca de la base .NET Frameworku.

Pero, sobre todo usted puede hacerlo usted 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 bastante fácil de escribir la interconectar mismo si lo necesita:

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

Después de haber leído recientemente el artículo Por qué copiar un objeto es una cosa terrible a hacer? , creo que esta pregunta debe clafirication adicional. Otras respuestas aquí proporcionan buenos consejos, pero aún así la respuesta no es completa - ¿por qué no ICloneable<T>

  1. Uso

    Por lo tanto, tiene una clase que lo implementa. Mientras que anteriormente tenía un método que quería ICloneable, que ahora tiene que ser genéricos para aceptar ICloneable<T>. Usted tendría que editarlo.

    A continuación, se podría haber conseguido un método que comprueba si un objeto is ICloneable. ¿Ahora que? No se puede hacer is ICloneable<> y como usted no sabe el tipo del objeto al tipo de compilación, no se puede hacer que el método genérico. En primer problema real.

    Así que hay que tener tanto ICloneable<T> y ICloneable, la antigua aplicación de esta última. Por lo tanto un ejecutor tendría que aplicar ambos métodos - object Clone() y T Clone(). No, gracias, ya tenemos suficiente diversión con IEnumerable.

    Como ya se ha señalado, existe también la complejidad de la herencia. Si bien puede parecer covarianza para resolver este problema, un tipo derivado necesita implementar ICloneable<T> de su propio tipo, pero ya hay un método con la misma firma (= parámetros, básicamente) - la Clone() de la clase base. La fabricación de su nueva interfaz método clone explícita no tiene sentido, se pierde la ventaja que buscó al crear ICloneable<T>. Así que añadir la palabra clave new. Pero no se olvide que también tendría que anular el Clone() clase base (la implementación tiene que permanecer uniforme para todas las clases derivadas, es decir, para devolver el mismo objeto desde todos los métodos de clonación, por lo que el método clone base tiene que ser virtual) ! Pero, por desgracia, no se puede ambos métodos override y new con la misma firma. La elección de la primera palabra clave, usted perdería el objetivo que quería tener al agregar ICloneable<T>. Chossing la segunda, se rompería la propia interfaz, por lo que los métodos que deben hacer el mismo retorno diferentes objetos.

  2. Punto

    ¿Quieres ICloneable<T> para la comodidad, pero la comodidad no es lo que las interfaces están diseñados para, su significado es (en programación orientada a objetos en general) para unificar el comportamiento de los objetos (aunque en C #, que se limita a unificar el comportamiento externo, por ejemplo, los métodos y propiedades, no sus funcionamientos).

    Si la primera razón no le ha convencido, sin embargo, se podría objetar que ICloneable<T> también podría trabajar de forma restrictiva, para limitar el tipo de volver del método clone. Sin embargo, el programador puede implementar desagradable ICloneable<T> donde T no es el tipo que se lleve a la práctica. Por lo tanto, para lograr su restricción, se puede añadir un buen limitación para el parámetro genérico:
    public interface ICloneable<T> : ICloneable where T : ICloneable<T>
    Sin duda, más restrictiva que el uno sin where, todavía no puede restringir que T es el tipo que está implementando la interfaz (que puede derivar de ICloneable<T> del curso que no lo implementa).

    Usted ve, incluso este objetivo no se puede lograr (la ICloneable original también fracasa en esto, hay una interfaz realmente puede limitar el comportamiento de la clase de aplicación).

Como se puede ver, esto demuestra haciendo la interfaz genérica es a la vez difícil de aplicar plenamente y también muy innecesario e inútil.

Pero volviendo a la pregunta, lo que realmente buscan es tener la comodidad cuando se clona 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 ofrece la comodidad y comportamiento previsto para los usuarios, pero también es demasiado tiempo para poner en práctica. Si no nos quieren tener el método "cómodo" devolver el tipo actual, es mucho más fácil tenersimplemente public virtual object Clone().

Así que veamos la solución "definitiva" - lo que en C # es realmente intented para dar Consolemos?

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

Es el nombre de Copiar de no chocar con los actuales Clon métodos (compilador prefiere propios métodos declarados del tipo sobre los de extensión). La restricción class está ahí para que 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 aplicar ICloneable en absoluto.

A pesar de que la cuestión es muy antigua (5 años a partir de la redacción de este respuestas :) y ya fue respondida, pero he encontrado este artículo responde a la pregunta bastante bien, lo comprueba aquí

EDIT:

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

  

Hay muchas referencias en la que señala a Internet a una entrada de blog 2003   por Brad Abrams - en el momento empleado en Microsoft - en la que algunos   Se discuten ideas sobre ICloneable. La entrada del blog se puede encontrar   en esta dirección: Ejecución ICloneable . A pesar de la engañosa   título, esta entrada de blog no llama a implementar ICloneable, principalmente   debido a la confusión superficial / profundo. El artículo termina en una recta   sugerencia: Si usted necesita un mecanismo de clonación, definir su propio clon, o   Copiar la metodología, y asegurarse de que usted documenta claramente si se trata de una   copia profunda o superficial. Un patrón apropiado es: public <type> Copy();

Un gran problema es que no podían restringir T para ser la misma clase. Fore ejemplo, lo que 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

Es una muy buena pregunta ... Se podría hacer su propia cuenta, sin embargo:

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

Andrey dice que es considerado un mal API, pero no he oído nada acerca de esta interfaz quedar obsoleta. Y eso sería romper toneladas de interfaces de ... El método Clone debe realizar una copia superficial. Si el objeto también proporciona copia profunda, un clon sobrecargado (bool de profundidad) puede ser utilizado.

EDIT:. Pattern i uso para "clonación" un objeto, está pasando un prototipo en el constructor

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

Esto elimina cualquier situación potencial de implementación del código redundante. Por cierto, hablando de las limitaciones de ICloneable, no es realmente hasta el propio objeto de decidir si un clon clon superficial o profunda, o incluso un clon parte superficial / profundo, en parte, se debe realizar? En caso de que realmente importa, siempre y cuando las obras objeto según lo previsto? En algunas ocasiones, una buena aplicación Clon muy bien podría incluir la clonación, tanto superficiales y profundas.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top