2

ジェネリック型「G」を持つクラスがあります

私のクラスモデルでは

public class DetailElement : ElementDefinition

このような方法があるとしましょう

        public void DoSomething<G>(G generic)
            where G : ElementDefinition
        {
            if (generic is DetailElement)
            {
                ((DetailElement)generic).DescEN = "Hello people"; //line 1
                //////
                ElementDefinition element = generic;
                ((DetailElement)element).DescEN = "Hello again"; //line 3
                //////
                (generic as DetailElement).DescEN = "Howdy"; //line 5
            }
            else
            {
                //do other stuff
            }
        }

コンパイラは 1 行目に 1 つのエラーを報告します。

Cannot convert type 'G' to 'DetailElement'

しかし、3行目はうまくいきます。5 行目に書かれたコードを実行することで、この問題を回避できます。

私が知りたいのは、コンパイラが 3 行目ではなく 1 行目のエラーを報告する理由です。

編集:フレームワークロジックの重要な部分が欠けているのではないかと心配しています

edit2: コンパイラ エラーの解決策は重要ですが、私の質問は、コンパイラが 3 行目ではなく 1 行目にエラーを報告する理由についてです。

4

4 に答える 4

7

が( )Gになるように制約されている場合は、先に進んで ElementDefinition にキャストできます。つまり、" " です。ただし、実行時以外の別のサブクラスである可能性があるため、型が不明で検証できないコンパイル時には許可されません。DetailElementwhere G : DetailElementG(ElementDefinition) genericGElementDefinitionDetailElement

3 行目では、キャスト元の型がであることわかっているElementDefinitionため、実行しているのはアップキャストだけです。コンパイラは、実行時にキャストが成功するかどうかを知りませんが、そこで信頼します。コンパイラは、ジェネリックをそれほど信頼していません。

5 行目のas演算子も null を返す可能性があり、その場合、コンパイラはその型が安全かどうかを静的にチェックしません。互換性のあるものだけでなく、どのタイプでas使用できますElementDefinition

ジェネリック型パラメーターとの間でキャストできますか? MSDN:

コンパイラでは、ジェネリック型パラメーターをオブジェクトまたは制約で指定された型に暗黙的にキャストすることしかできません。

このような暗黙のキャストは、コンパイル時に非互換性が発見されるため、もちろんタイプ セーフです。

コンパイラでは、ジェネリック型パラメーターを任意のインターフェイスに明示的にキャストできますが、クラスにはキャストできません。

   interface ISomeInterface {...}
   class SomeClass {...}
   class MyClass<T> 
    {
      void SomeMethod(T t)
       {
         ISomeInterface obj1 = (ISomeInterface)t;//Compiles
         SomeClass      obj2 = (SomeClass)t;     //Does not compile
       }
    }

ただし、一時オブジェクト変数を使用して、ジェネリック型パラメーターから他の型へのキャストを強制できます。

 void SomeMethod<T>(T t) 
  { object temp = t;
    MyOtherClass obj = (MyOtherClass)temp;  
  }

言うまでもなく、このような明示的なキャストは危険です。ジェネリック型パラメーターの代わりに使用される具体的な型が、明示的にキャストする型から派生していない場合、実行時に例外がスローされる可能性があるためです。

キャスト例外の危険を冒す代わりに、isoras演算子を使用することをお勧めします。isジェネリック型パラメーターがクエリされた型である場合、演算子は true を返し、as型に互換性がある場合はキャストを実行し、それ以外の場合は null を返します。

public void SomeMethod(T t)
 {
   if(t is int) {...}

   string str = t as string;
   if(str != null) {...}
 }
于 2008-10-08T17:43:16.527 に答える
1

一般的に、アップキャストはコードの匂いです。メソッドのオーバーロードによって回避できます。これを試して:

public void DoSomething(DetailElement detailElement)
{
    // do DetailElement specific stuff
}

public void DoSomething<G>(G elementDefinition)
    where G : ElementDefinition
{
    // do generic ElementDefinition stuff
}

次に、次のコードを使用して、メソッドのオーバーロードを利用できます。

DetailElement foo = new DetailElement();

DoSomething(foo); // calls the non-generic method
DoSomething((ElementDefinition) foo); // calls the generic method
于 2008-10-08T19:13:26.220 に答える
1

where句は「where G : DetailElement」であるべきではありませんか?

あなたが書いたコードでは、DetailElement は ElementDefinition ですが、ElementDefinition は必ずしも DetailElement ではありません。したがって、暗黙の変換は違法です。

このメソッドに渡す可能性のある ElementDefinition の他のタイプはありますか? その場合、それらを DetailElement インスタンスにキャストしようとすると、例外がスローされます。

編集:

コード リストを変更したので、そのコード ブロックを入力する前に、タイプをチェックして本当に DetailElement であることを確認していることがわかります。残念ながら、実際には、自分で型を確認済みであっても、暗黙的にダウンキャストすることはできません。ブロックの先頭に「as」キーワードを使用する必要があると思います。

DetailElement detail = generic as DetailElement;
if (detail == null) {
   // process other types of ElementDefinition
} else {
   // process DetailElement objects
}

いっそのこと、ポリモーフィズムを使用して、各種類の ElementDefinition が独自の DoSomething メソッドを定義できるようにし、CLR に型チェックとメソッド呼び出しを任せてみませんか?

于 2008-10-08T17:42:38.010 に答える
0

心配しているElementDefinitionsがたくさんある場合、これはもう少しコードにつながりますが、おそらく、関係のない最も洗練されたものは、ナンセンスです。

    public void DoSomething<G>(G generic)
        where G : ElementDefinition
    {
        DetailElement detail = generic as DetailElement;
        if (detail != null)
        {
            detail.DescEN = "Hello people";
        }
        else
        {
            //do other stuff
        }
    }

一時オブジェクト変数の代わりに、そのような情報が必要なときに使用した別の可能な解決策。

DetailElement detail = (DetailElement)(object)generic;

動作しますが、as形式がおそらく最適です。

于 2008-10-08T18:37:55.510 に答える