12157


下記コードのビルドが通らず困っています。

[code]

    public class TestClass : IBase, IExpansion1, IExpantion2
    {
        public T Get<T>() where T : IBase
        {
            return this;
        }
    }
    
    public interface IBase {}
    public interface IExpansion1 : IBase {}
    public interface IExpantion2 : IBase {}
    
[/code]

具体的には _return this;_ のところで型変換ができないと怒られます。

思惑としては、  
他のクラスがTestClassの機能を使うのに実体そのまま使うのではなく、  
利用クラスごとに適切なinterface（IExpansion1, IExpantion2）を取得し、  
それを通して使って欲しいと考えています。

その中で、interfaceの提供に一々メソッドを作るのもなんだかなと思うので、  
ジェネリックなGetメソッドを作りIBaseで型制約をかけようとしたのですが、  
うまくいきません。

まず、これがビルドが通らない理由ですが  
「TestClassが全てのIBaseを実装/継承しているclass/interfaceを継承/実装しているとは限らないから」  
なのかなと思ったのですが、その認識は正しいでしょうか？

そうだとしたら、上記のような思惑がある場合、どのような設計にしたら良いのでしょうか？  
interface一つ一つに取得メソッドを作るしかないのでしょうか？

よろしくお願いします。


CC BY-SA 3.0
2015-07-08T08:44:05.013
12154
2015-07-08T13:25:33.223
10450
question
c#
C#のインターフェースについて


> 「TestClassが全てのIBaseを実装/継承しているclass/interfaceを継承/実装しているとは限らないから」  
>  なのかなと思ったのですが、その認識は正しいでしょうか？

そういう理由ではありません。`where`はあくまでクラス継承、インターフェース実装しか保証できません。

[code]

    class Other : IBase {}
    
[/code]

が存在するとして

[code]

    TestClass t;
    Other o = t.Get<Other>();
    
[/code]

に失敗するのはわかりますよね？


CC BY-SA 3.0
2015-07-08T09:20:06.647
12156
2015-07-08T09:20:06.647
4236
12154
answer


一度アップキャストすれば変換可能です。

[code]

    return (T)(object)this;
    
[/code]

## 追記

型安全ですが現状のC#でビルド時に検出するのは難しいです。次期バージョンなら出来なくはないですが。

実行時に検出するための良くある仕様は「キャスト不能時に`null`を返す」という条件を増やすことです。その場合は

[code]

    if (typeof(T).IsAssignableFrom(GetType()))
    {
        return (T)(object)this;
    }
    return default(T);
    
[/code]

や、あるいは`T`に`class`制約を付けて

[code]

    return this as T;
    
[/code]

と実装します。


CC BY-SA 3.0
2015-07-08T09:21:50.570
12157
2015-07-08T10:38:54.353
2015-07-08T10:38:54.353
5750
5750
12154
answer


TestClassがキャスト可能な型は、TestClass、IBase, IExpansion1, IExpantion2,objectの5種類です。

[code]

    public T Get<T>() where T : IBase
    {
        return this;
    }
    
[/code]

これだと、TはIBaseを継承したクラス何でも指定可能なので、上記以外の型も指定できてしまいます。  
その為ビルドエラーになっています。

標準のas演算子でキャスト可能か調べる事ができますが、これでは駄目でしょうか？  
<https://msdn.microsoft.com/ja-jp/library/cscsdfbt.aspx>

[code]

    var test = new TestClass();
    var base = test as IBase;
    var expansion1 = test as IExpansion1;
    var expansion2 = test as IExpansion2;
    
[/code]

（キャスト不可能な型を指定した場合の結果はnull）


CC BY-SA 3.0
2015-07-08T13:10:24.307
12162
2015-07-08T13:25:33.223
2015-07-08T13:25:33.223
49
4191
12154
answer