【.NET Framework 4.5】 IListがIReadOnlyListを継承してない理由。
- 2013.02.21 Thursday
- 21:51
IReadOnlyCollection<T>が登場した時も同じ疑問が芽生えたのですが、 IList<T>がIReadOnlyList<T>を継承してない、何でだろうって思ったことあります。
ということで「IList<T>がIReadOnlyList<T>を継承してない理由」を考えてみたいと思います。
public interface IList<T> : ICollection<T>, IEnumerable<T>, IEnumerable
{
int IndexOf(T item);
void Insert(int index, T item);
void RemoveAt(int index);
T this[int index] { get; set; }
}
public interface IReadOnlyList<out T> : IReadOnlyCollection<T>, IEnumerable<T>, IEnumerable
{
T this[int index] { get; }
}
.NET Framework 4.5では上記のように定義されているので、
public interface IList<T> : IReadOnlyList<T>, ICollection<T>, IEnumerable<T>, IEnumerable
{
int IndexOf(T item);
void Insert(int index, T item);
void RemoveAt(int index);
T this[int index] { get; set; }
}
継承関係にある二つのインターフェイスに同名のプロパティが定義されていて、とても気持ち悪い状態になり、コンパイル時に警告が出るようにはなります。ただし、このインターフェイスのみを考えるとコンパイルは通ります。問題なさそうです。ただし、実際にはIList<T>がIReadOnlyList<T>から派生すると悲劇が生まれるます。
ちょいと簡単にするために一つインターフェイスを定義してみます。
public interface IDemo
{
int Get();
void Set(int value);
}
これに対して実装クラスをあったとします。
public class DemoImpl : IDemo
{
private int _value;
int IDemo.Get()
{
return _value;
}
void IDemo.Set(int value)
{
_value = value;
}
}
この時点ではコンパイルは通っています。これに対して、IDemoにIReadOnlyDemoを追加してみます。
public interface IDemo : IReadOnlyDemo
{
int Get();
void Set(int value);
}
public interface IReadOnlyDemo
{
int Get();
}
なんと!コンパイルが通らなくなります(あたりまえですが)
これを.NET Frameworkに置き換えて話すと、.NET Framework 4.0までのIList<T>とList<T>がありました。.NET Framework 4.5(仮)でIList<T>をIReadOnlyList<T>から派生させました。List<T>に問題が発生するのでList<T>に修正を加えます。そこでは、.NET Frameworkのクラスライブリーの責任の範囲内なので、実装できます。しかし、IList<T>から独自に実装していたクラスがあった場合、.NET Framework 4.5にターゲットを変更した場合、とたんにコンパイルエラーになってしまいます。これは、いただけないです。拡張したつもりが、実際にはアセンブリ間では変更になってしまいます。
だったら、同時期にIList<T>とIReadOnlyList<T>が提供されていたら問題はなかったのか
?という疑問がわいてきます。が!ちょっと私わかりません。もう少し考えてから続きは書きたいと思います。
私は設計者ではなくあくまで利用者なのです。「継承しているべき」と考えたいたのではなく、「継承されているもの」だと勘違いして、実際に利用しようとした際に、勘違いしていることに気づいたというのが経緯です。そこで、継承関係の妥当性を抜きにして、保守性の観点から弊害があることに気づいたので、とりあえずその部分だけ記事にしてしまいました。たしかに、この部分だけを抜き出して書くと、まるで継承してることが正しいと言ってるように見えます。が、それは誤解なのです。
継承関係の妥当性に関しては歴史的経緯と現状と私の能力と知識では結論を見いだすことができていません。思考を進めていくと、名前がおかしのか、メンバーがおかしいのか、今さら変更できないのかとか、色々な要因が干渉し私の能力では論理的に説明できなくなってしまいます。
なぜ勘違いしたのかは、気が向いたら記事にします。おそらく私と同じような頭の構造を持った人がときどき犯すミスがあり、バグを入れる一つの要因となるものだと思います参考になると思います。
名前がおかしいというのならその通りでIReadableListだったらそれをIListが継承するのも結構でしょう(そんなことをして何の意味があるのかはともかく)。
しかし,IReadOnlyListという名前で作っているということはReadOnlyなListであることを前提としているのは明らかであり,そのように使うべきです。IListに使うのは明らかにその意図を無視しています。
ICloneableを実装したクラスでインスタンス自身とそのインスタンスのクローンをまとめたタプルを返すCloneメソッドを作ることは出来ますが,そんなICloneableを作るのはまともな開発者ではありません。
クラス名だってプログラミングの一部なのでありコンパイルが通るならば良いわけではないのではないでしょうか。
こんばんわ、ずいぶん昔の書き込みにコメントをいただきありがとうございます。当時、自分が何を考えていたのかサッパリ思い出しませんが、きっと背理法的な論法でなにかを納得しようとしていたのではないかと思われます。
未だに継承とか継承関係とか抽象化とかものごとの関係性に対してのセンテンスがまるで理解できず、よくわからんわーって思いながらもやっています。というわけで、Genさんが書かれている内容を理解するのはもう少し時間がかかりそうです。
ちなみに一言だけ意見させていただきたいことは
> クラス名だってプログラミングの一部なのでありコンパイルが通るならば良いわけではないのではないでしょうか。
私はコンパイルが通ればなんでも良いなんて思っていませんよ?