ICloneable<T>
ジェネリックが存在しない特定の理由はありますか?
何かを複製するたびにキャストする必要がなければ、はるかに快適です。
Andrey の返信 (+1 に同意します) に加えて、ICloneable
完了したら、明示的な実装を選択して、パブリックClone()
に型指定されたオブジェクトを返すようにすることもできます。
public Foo Clone() { /* your code */ }
object ICloneable.Clone() {return Clone();}
もちろん、ジェネリックICloneable<T>
- 継承には 2 つ目の問題があります。
私が持っている場合:
public class Foo {}
public class Bar : Foo {}
そして、私は実装しICloneable<T>
ましたが、実装しICloneable<Foo>
ますか? ICloneable<Bar>
? あなたはすぐに多くの同一のインターフェースを実装し始めます... キャストと比較してください...そしてそれは本当に悪いことですか?
ICloneable は、結果がディープ コピーかシャロー コピーかを指定しないため、現在では不適切な API と見なされています。これが、彼らがこのインターフェースを改善しない理由だと思います。
おそらく、型指定された複製拡張メソッドを実行できますが、拡張メソッドは元のメソッドよりも優先度が低いため、別の名前が必要になると思います。
インターフェースを実装する以外に、インターフェースで正確に何をしますか? インターフェイスは通常、キャストする場合 (つまり、このクラスが「IBar」をサポートする場合)、またはそれを取るパラメーターまたはセッターがある場合 (つまり、「IBar」を取る場合) にのみ役立ちます。ICloneable を使用して、フレームワーク全体を調査しましたが、その実装以外の単一の使用法を見つけることができませんでした。また、実装以外のことを行う「現実世界」での使用法を見つけることもできませんでした (アクセスできる約 60,000 のアプリで)。
「複製可能な」オブジェクトに実装させたいパターンを強制したいだけなら、それはまったく問題のない使い方です - そして先に進んでください。また、自分にとって「複製」が何を意味するか (つまり、深いか浅いか) を正確に決定することもできます。ただし、その場合、私たち (BCL) がそれを定義する必要はありません。関連のないライブラリ間で抽象化として型指定されたインスタンスを交換する必要がある場合にのみ、BCL で抽象化を定義します。
デビッド・キーン (BCLチーム)
「なぜ」という質問は無用だと思います。非常に便利なインターフェース/クラス/その他がたくさんありますが、.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();
}
必要に応じて、自分でインターフェイスを作成するのは非常に簡単です。
public interface ICloneable<T> : ICloneable
where T : ICloneable<T>
{
new T Clone();
}
最近記事を読んで、なぜオブジェクトをコピーするのはひどいことなのですか? 、この質問には追加の説明が必要だと思います。ここでの他の回答は良いアドバイスを提供しますが、それでも回答は完全ではありません-なぜないのICloneable<T>
ですか?
使用法
したがって、それを実装するクラスがあります。以前は必要なメソッドがありましたがICloneable
、現在では受け入れるにはジェネリックである必要がありますICloneable<T>
。編集する必要があります。
次に、 object かどうかをチェックするメソッドを取得できますis ICloneable
。今何?できませんしis ICloneable<>
、コンパイル時のオブジェクトの型がわからないため、メソッドをジェネリックにすることはできません。最初の本当の問題。
したがって、前者が後者を実装するICloneable<T>
との両方が必要です。ICloneable
したがって、実装者は と の両方のメソッドを実装する必要がありobject Clone()
ますT Clone()
。いいえ、ありがとうございIEnumerable
ます。
すでに指摘したように、継承の複雑さもあります。共分散はこの問題を解決するように見えるかもしれませんが、派生型はICloneable<T>
独自の型を実装する必要がありますが、基本Clone()
クラスの同じシグネチャ (= パラメータ、基本的に) を持つメソッドが既に存在します。新しいクローン メソッド インターフェイスを明示的にすることは無意味です。作成時に求めた利点が失われますICloneable<T>
。new
というわけでキーワード追加。Clone()
ただし、基本クラスもオーバーライドする必要があることを忘れないでくださいvirtual
。override
しかし、残念ながら、両方を行うことはできません。new
同じ署名を持つメソッド。最初のキーワードを選択すると、追加する際に望んでいた目標が失われますICloneable<T>
。2 番目のものを選択すると、インターフェイス自体が破損し、同じことを行うメソッドが異なるオブジェクトを返すことになります。
点
あなたICloneable<T>
は快適さを望んでいますが、快適さはインターフェイスが設計されているものではありません.その意味は(一般的にOOPで)オブジェクトの動作を統一することです.彼らの働き)。
最初の理由がまだ納得できない場合はICloneable<T>
、clone メソッドから返される型を制限するために、制限的に機能する可能性があることに異議を唱えることができます。ただし、厄介なプログラマーはICloneable<T>
、 T がそれを実装している型ではない場合に実装できます。したがって、制限を達成するために、ジェネリック パラメーターに素敵な制約を追加
public interface ICloneable<T> : ICloneable where T : ICloneable<T>
できwhere
ます。それを実装します)。ICloneable<T>
ご覧のとおり、この目的でさえ達成できませんでした (オリジナルICloneable
もこれで失敗しました。実装クラスの動作を真に制限できるインターフェイスはありません)。
ご覧のとおり、これは、ジェネリック インターフェイスを完全に実装するのが難しいだけでなく、実際には不要で役に立たないことを証明しています。
しかし、質問に戻りますが、あなたが本当に求めているのは、オブジェクトのクローンを作成するときに快適であることです。それには 2 つの方法があります。
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();
}
}
このソリューションは、快適さと意図した動作の両方をユーザーに提供しますが、実装するには時間がかかりすぎます。「快適な」メソッドで現在の型を返したくない場合は、public virtual object 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;
}
現在のCloneメソッドと衝突しないようにCopyという名前が付けられています (コンパイラは、拡張メソッドよりも型自体の宣言されたメソッドを優先します)。制約は速度のためにあります (null チェックなどは必要ありません)。class
これで、作らない理由が明確になることを願っていICloneable<T>
ます。ただし、ICloneable
まったく実装しないことをお勧めします。
質問は非常に古く(この回答を書いてから5年:)、すでに回答されていますが、この記事が質問に非常によく回答していることがわかりました。ここで確認してください
編集:
質問に答える記事からの引用は次のとおりです (記事全体を必ず読んでください。他にも興味深いことが含まれています)。
Brad Abrams (当時 Microsoft に勤務していた) による 2003 年のブログ投稿を指すインターネット上の参照が多数あり、そこでは ICloneable に関するいくつかの考えが議論されています。ブログ エントリは、Implementing ICloneableのアドレスにあります。誤解を招くようなタイトルですが、このブログ エントリでは ICloneable を実装しないよう呼びかけています。この記事は次のような単純な提案で締めくくられています。クローン作成メカニズムが必要な場合は、独自のクローンまたはコピー方法を定義し、それがディープ コピーかシャロー コピーかを明確に文書化するようにしてください。適切なパターンは次のとおりです。
public <type> Copy();
大きな問題は、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
それは非常に良い質問です.しかし、あなたはあなた自身のものを作ることができます:
interface ICloneable<T> : ICloneable
{
new T Clone ( );
}
Andrey は、これは悪い API と見なされていると言っていますが、このインターフェースが非推奨になったという話は聞いたことがありません。そして、それは多くのインターフェースを破壊します... Clone メソッドは浅いコピーを実行する必要があります。オブジェクトがディープ コピーも提供する場合は、オーバーロードされた Clone ( bool deep ) を使用できます。
編集:オブジェクトの「複製」に使用するパターンは、コンストラクターでプロトタイプを渡しています。
class C
{
public C ( C prototype )
{
...
}
}
これにより、冗長なコード実装の可能性がなくなります。ところで、ICloneable の制限について話していますが、浅いクローンと深いクローン、または部分的に浅い/部分的に深いクローンを実行するかどうかを決定するのは、実際にはオブジェクト自体にかかっているのではないでしょうか? オブジェクトが意図したとおりに機能する限り、本当に気にする必要がありますか? 場合によっては、適切な Clone 実装に、浅いクローン作成と深いクローン作成の両方が含まれている可能性があります。