C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Cloneの戻り値の型


(過去ログ 8 を表示中)

[トピック内 13 記事 (1 - 13 表示)]  << 0 >>

■8039 / inTopicNo.1)  Cloneの戻り値の型
  
□投稿者/ 一羽 二等兵(12回)-(2006/11/03(Fri) 00:04:54)

分類:[C#] 


分類:[C#] 

検索ワードが悪い為か、良い情報が見つからなかったのでポストします。
既出のような気もするのですが……、申し訳ありません。

実装クラスClassA、ClassB、ClassCの継承元である抽象クラス(AbstractClass)があります。
実装クラスすべてにIClonableを継承したかったため、AbstractClassにてIClonableを継承しました。そして、以下のように記述しました。
------------------------------------------------------
public abstract object Clone();
object IClonable.Clone()
{
  return this.Clone();
}
------------------------------------------------------
Clone()で行うべきことは各クラスで異なっているため、抽象クラスのClone()をabstractにして、ClassA〜ClassCでClone()をoverrideしようとしたのです。
しかし、(考えてみれば当たり前ですが)、この方法ではいずれのクラスもClone()で得られるものはobject型で、Clone()を呼び出した側で各クラスにキャストしなければなりません。

(1) ClassAのクローンがClassA型でないのはどうしても納得いきません。
(2) とはいえ、各実装クラスでIClonableを継承し、それぞれのクラスの型を返すClone()が実装するのは、スマートとは思えません。

これは(1)をぐっと堪えていくしかないのでしょうか。
「羊」のクローンを作ったら「生き物」が出来た、といった感じで、とても気持ち悪いのですが、そんなのにこだわるのは私ぐらいなのでしょうか……?

何か良い情報、あるいは、アドバイスをお願いしますm(_ _)m

0
引用返信 編集キー/
■8040 / inTopicNo.2)  追記
□投稿者/ 一羽 二等兵(13回)-(2006/11/03(Fri) 00:15:17)

分類:[C#] 

IClonable.Clone()の戻り値がobjectであることについては、疑問・不満はありません。インスタンスをインタフェースとして扱う以上、型がobjectになるのは普通(?)かと思うのです。
しかし、インスタンスをその実装クラスとして扱っているときに、実装クラス以外のものがClone()で返ってくるのが……(渋面)

0
引用返信 編集キー/
■8041 / inTopicNo.3)  追記2
□投稿者/ 一羽 二等兵(14回)-(2006/11/03(Fri) 00:36:56)

分類:[C#] 

すみません、なぜか編集できないので追記ですm(_ _)m

何をしたいのか書き忘れていました、すみません。

・ある抽象クラス(AbstractClass)を継承するすべてのクラスでIClonableを実装することを義務付けたい
・Clone()で実装クラスのインスタンスを得たい
を両立させる良い方法を探しています。

0
引用返信 編集キー/
■8043 / inTopicNo.4)  Re[3]: 追記2
□投稿者/ 中 博俊 二等兵(3回)-(2006/11/03(Fri) 00:41:14)

分類:[C#] 

> ・ある抽象クラス(AbstractClass)を継承するすべてのクラスでIClonableを実装することを義務付けたい
> ・Clone()で実装クラスのインスタンスを得たい

わかってるでしょう。(^^;
無理なんです。


0
引用返信 編集キー/
■8044 / inTopicNo.5)  Re[2]: 追記
□投稿者/ επιστημη 中将(271回)-(2006/11/03(Fri) 00:50:44)
επιστημη さんの Web サイト

分類:[C#] 

> しかし、インスタンスをその実装クラスとして扱っているときに、実装クラス以外のものがClone()で返ってくるのが……(渋面)

ふたっつ用意するんじゃダメですか?

using System;

public class hoge : ICloneable {
private string id_;
public hoge(string id) { id_ = id; }
public override string ToString() {
return string.Format("hoge({0})",id_);
}

// コレと
object ICloneable.Clone() {
Console.WriteLine("object Clone()");
return new hoge(id_);
}

// コレ
public hoge Clone() {
Console.WriteLine("hoge Clone()");
return new hoge(id_);
}

public static void Main() {
hoge h = new hoge("ほげ");
object o = ((ICloneable)h).Clone(); // object Clone
Console.WriteLine(o);
hoge c = h.Clone(); // hoge Clone
Console.WriteLine(c);
}
}


0
引用返信 編集キー/
■8045 / inTopicNo.6)  Re[4]: 追記2
□投稿者/ ghost_shell 二等兵(8回)-(2006/11/03(Fri) 01:04:14)
ghost_shell さんの Web サイト

分類:[C#] 

私も「(インターフェースにキャストしないと呼べない)明示的な実装」を使えば同じシグニチャで2つメソッドが作れるから・・・と考えたけれど、提示された条件では無理ですね。

(まるっきしお二人の繰り返しだー orz)

0
引用返信 編集キー/
■8046 / inTopicNo.7)  Re[5]: 追記2
□投稿者/ 中 博俊 二等兵(4回)-(2006/11/03(Fri) 01:19:32)

分類:[C#] 

ICloneableインターフェイス自身Objectなのであまりに困りものです。
ただICloneableで受けない限りは問題ないでしょう。
ICloneable<T>の
public T Clone();
はいけたっけ?



0
引用返信 編集キー/
■8047 / inTopicNo.8)  Re[6]: 追記2
□投稿者/ ghost_shell 二等兵(9回)-(2006/11/03(Fri) 01:20:25)
ghost_shell さんの Web サイト

分類:[C#] 

> ・Clone()で実装クラスのインスタンスを得たい
実装クラスの型で取得したいのだったらインターフェースは無関係。だからICloneableで使われているCloneという名前は使わない方がいいと思う。

とりあえず書いてみたコード。

abstract class AbstractClass : ICloneable
{
public abstract AbstractClass Clone();

#region ICloneable メンバ

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

#endregion
}

class ClassA : AbstractClass
{
private ClassA Clone_()
{
return (ClassA) Clone();
}

public override AbstractClass Clone()
{
throw new Exception("The method or operation is not implemented.");
}


0
引用返信 編集キー/
■8049 / inTopicNo.9)  Re[1]: Cloneの戻り値の型
□投稿者/ NyaRuRu 二等兵(8回)-(2006/11/03(Fri) 01:50:00)

分類:[C#] 

No8039に返信(一羽さんの記事)
> (1) ClassAのクローンがClassA型でないのはどうしても納得いきません。

とりあえず気をつけるべきは以下の条件が満たされているかどうかでは?
ICloneable x;
Debug.Assert( x.GetType() == x.Clone().GetType() );
原則的にキャストしてもインスタンスの型 (GetType() で返る型) が変わるわけではないので,「羊」のクローンはやっぱり「羊」が返ってきているのだと思います.

> (2) とはいえ、各実装クラスでIClonableを継承し、それぞれのクラスの型を返すClone()が実装するのは、スマートとは思えません。

多分,一羽さんは各実装クラスが単純にClone可能な場合のみを想定されているのでしょうが,世の中自動的には「Clone」が定義出来ないこともあることに注意してください.
恐らく暗黙のうちに何らかの条件を仮定しているかと思いますがそれを元に Reflection 等を使用して記述自体は汎用化できることがあります.
例えば,フィールド全てが ICloneable と仮定しているのであれば,Reflection で各フィールドについて Clone してやるという処理を書くことで,フィールド数が増えてもそのまま使えます.
逆に自動化できそうにない奴はつまるところケースバイケースに対処が求められる実例ということで,今回のような「一般的に〜できませんか?」という議論では予め除いておかないと答えが出ないことになります.

0
引用返信 編集キー/
■8050 / inTopicNo.10)  Re[6]: 追記2
□投稿者/ NyaRuRu 二等兵(9回)-(2006/11/03(Fri) 01:52:59)

分類:[C#] 

No8046に返信(中 博俊さんの記事)
> ICloneableインターフェイス自身Objectなのであまりに困りものです。
> ただICloneableで受けない限りは問題ないでしょう。
> ICloneable<T>の
> public T Clone();
> はいけたっけ?

この辺参照のこと
http://d.hatena.ne.jp/NyaRuRu/20060121/p2

0
引用返信 編集キー/
■8055 / inTopicNo.11)  無理のようで(^^;
□投稿者/ 一羽 二等兵(15回)-(2006/11/03(Fri) 17:37:03)

分類:[C#] 

みなさん、ご意見ありがとうございますm(_ _)m
とりあえず、「無理」という結論にして、Cloneの戻り値はobjectかAbstractClassのどちらかで定義しようと思います。

NyaRuRuさまがおっしゃったリフレクションに関しては、リフレクション自体に対する知識が不足しているため、どうすればよいのか現段階ではイメージがわきません(-_-;)
もう少し勉強して理解できるようになってから、上記のクラスをリファクタリングすることにします。

----とはいえ、どうにも不満は残りまして。

(※最初に例示した名前だとわかりにくいので名前を変えちゃいます)

ある複製可能なクラス(仮称:ClassLanchSet)が所有するクラス(仮称:ClassSarada、ClassRice、ClassSoup)を複製可能とする手法として、
ClassSarada、ClassRice、ClassSoupの継承元であるAbstractClassDishにIClonableを実装させるのがスマートだと思ったのですが、
この方法ではClassSaradaを複製して得られるものが「サラダ」とラベルされているのではなく「料理」とラベルされているため、
いちいち明示的にキャストして(ラベルを張り替えて)使うことになり、使う側からするとスマートでない感じがします。

NyaRuRuさまの言うとおり世の中すべてのクラスがClonableではないのですが、Clonableであることを強要する場面(ランチセットに含まれる料理はすべて複製できないと困りますよね)で、どうやれば「必ずClonableで」「使う側から見てもスマート」な実装ができるものか……。

まだしばらく悩もうと思います。(コードのほうは締め切りがあるので進めざるを得ませんが)
解決マークはしばらく保留させてくださいm(_ _)m

0
引用返信 編集キー/
■8056 / inTopicNo.12)  Re[3]: 無理のようで(^^;
□投稿者/ NyaRuRu 二等兵(10回)-(2006/11/03(Fri) 18:16:04)

分類:[C#] 

2006/11/03(Fri) 18:20:38 編集(投稿者)

No8055に返信(一羽さんの記事)
>いちいち明示的にキャストして(ラベルを張り替えて)使うことになり、使う側からす>るとスマートでない感じがします。

私の場合,そこまで拘りがあるわけではないのですが,こういうことをしたいということですか?

<pre>
public abstract class AbstractClassDish : ICloneable
{
abstract protected object CloneImpl();
object ICloneable.Clone()
{
return this.CloneImpl();
}
}

public sealed class ClassSarada : AbstractClassDish
{
protected override object CloneImpl()
{
return this.Clone();
}
public ClassSarada Clone()
{
throw new Exception("The method or operation is not implemented.");
}
}

public sealed class ClassRice : AbstractClassDish
{
public readonly string RiceName;
public ClassRice(string name)
{
this.RiceName = name;
}
protected override object CloneImpl()
{
return this.Clone();
}
public ClassRice Clone()
{
return new ClassRice(RiceName);
}
}

public sealed class ClassSoup : AbstractClassDish
{
protected override object CloneImpl()
{
return this.Clone();
}
public ClassSoup Clone()
{
throw new Exception("The method or operation is not implemented.");
}
}

static class Program
{
static void Main(string[] args)
{
ClassRice rice = new ClassRice("コシヒカリ");
ClassRice riceClone = rice.Clone();
object objectClone = (rice as ICloneable).Clone();
}
}
</pre>

>NyaRuRuさまの言うとおり世の中すべてのクラスがClonableではないのですが、
>Clonableであることを強要する場面(ランチセットに含まれる料理はすべて
>複製できないと困りますよね)
>で、どうやれば「必ずClonableで」「使う側から見てもスマート」な実装が
>できるものか……。

強要するにはいくつか方法があって,確かにコンパイル時の型チェックもその1つです.
しかし,ユニットテストやコード検証ツールの併用といった方法もあり,必ずしもキャストの排除に全ての労力を注ぐ必要はないように思います.
実装漏れみたいなのを本当に不安視するなら,コンパイル後のテストでもある程度チェックしておくべきかと思いますが,そういうことはされていますか?


0
引用返信 編集キー/
■8148 / inTopicNo.13)  ご指導ありがとうございます。
□投稿者/ 一羽 二等兵(17回)-(2006/11/08(Wed) 02:45:55)

分類:[C#] 

すっかり遅くなってしまい、申し訳ありません。
NyaRuRuさまの提示されたコードに、目からうろこが落ちました。IConable実装の名前をCloneに固定しなくても良いのですね(IClonable.Cloneでない方)。
実際のClone処理を行う別名関数(CloneImp)でabstract宣言しておいて、継承先でそのクラスのインスタンスを返すCloneという名前の関数を用意すれば、すっきりしそうです。

実装漏れを不安視し、コンパイルエラーで弾きたかったのは、このコードをメンテナンスする人が自分以外である可能性が高く、なおかつ、ある意味で「信用できない」人である可能性が中くらいであるからです。
ユニットテストやコード中のコメントなどの対策を(なんとか)行っています。自分や、こちらにいらっしゃるような方々であれば、それで十分かも、と思うのですが、メンテナンスする可能性のある人の中には、ユニットテストを不要(面倒だから)と言って更新/追加/実行しなかったり、「コンパイルが通った=単体テストをパスした」と言ったりする人が、そこそこいるので……。(すみません、愚痴になりましたm(_ _)m

解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -