1

次のようなコードがあります:(この質問には役立たないため、メソッドを削除しました)

public abstract class A { }
public interface I { }
public class C : A , I { }
public class Program
{
    static void Update<T>(List<T> l,A a,I i,C c)
    {

        l.Add((T)a);//Error
        l.Add((T)i);
        l.Add((T)c);//Error
    }
}

キャストは、抽象クラスと具象クラスのコンパイル時に失敗しますが、インターフェースでは失敗します。

コンパイラをだますようなことができることは知っていl.Add((T)(object)a);ますが、インターフェイスを T にキャストすることがなぜ機能するのか理解できません (すべてがうまくいかなかった場合、ある種の型チェックエラーと見なすことができます)。

Javaで試してみましたが、すべて動作します。

public static <T> void update(List<T> l,C c,I i,A a){
    l.add((T)c);//ok
    l.add((T)i);//ok
    l.add((T)a);//ok
}

それは、C#コンパイラのやり方が原因なのか、それともOOPの概念が欠けているからですか?

4

4 に答える 4

1

Tコンパイラがコードを理解できるようにするには、 with キーワードwhereを次のように制約する必要があります。

public abstract class A { }
public interface I { }
public class C : A, I { }
public class Program
{
    static void Update<T>(List<T> l, A a, I i, C c)
        // HERE IS THE CHANGE
        where T: C
    {

        l.Add((T)a);//Error
        l.Add((T)i);
        l.Add((T)c);//Error
    }
}

これは、Tクラスではなく、デフォルトですべてになる可能性があり、コードのすべてのバリアントが機能するように制約した後でのみ可能になるためです。

私が言及したようにメソッドを制約する場合にのみこれをコンパイルできるので、できる唯一の使用法は次のとおりです。

var c = new C();
Update<C>(new List<C>(), c as A, (I)c, c);

これは役に立たないので、コードを書き直す必要があります。インターフェイスを増やし、基本クラスを減らすべきだと思います。

于 2012-11-22T07:57:15.153 に答える
1

コンパイル時には、インターフェイスを実装するクラスIが実際に型になるかどうかは不明であるTため、コンパイラは文句を言うことができません。ただしT、コードではバインドされておらず、コンパイラはT、コードをより安全にするために境界を宣言する必要があると不平を言います。

コンパイル時の安全性を回避し、次のように呼び出しサイトですべてが機能することを願っています。

  l.Add((T)(object)a);// no compile time Error but possibly at runtime
  l.Add((T)i);  //no compile time Error but possibly at runtime
  l.Add((T)(object)c);// no compile time Error but possibly at runtime

しかし、コンパイル時のチェックを犠牲にするのは得策ではありません。

T代わりに、たとえば次のように境界を宣言します。

static void Update<T>(List<T> l, A a, I i, C c)  where T: C {

  l.Add((T)a);// no compile time Error and less likely a runtime error
  l.Add((T)i);  //no compile time Error but still possibly at runtime
  l.Add((T)c);// no compile time Error and less likely a runtime error
}

そうしないと、これがどのようにうまくいかないかは簡単にわかります

呼び出すとUpdate<string>(stringlist, a, i, c)、A、I、C のいずれも文字列にキャストできないため、それ以外の場合は壊れます。

于 2012-11-22T07:57:59.117 に答える
0

6.2.7 C# 言語仕様 (「型パラメーターを含む明示的な変換」) を参照してください。説明があります。

"

次の明示的な変換は、特定の型パラメーター T に対して存在します。

  • 任意のインターフェイス タイプから T へ。実行時に T が値型の場合、変換はボックス化解除変換として実行されます。それ以外の場合、変換は明示的な参照変換または ID 変換として実行されます。

上記の規則では、制約のない型パラメーターから非インターフェイス型への直接の明示的な変換は許可されていませんが、これは驚くべきことです。このルールの理由は、混乱を防ぎ、そのような変換のセマンティクスを明確にするためです。たとえば、次の宣言を考えてみましょう。

class X<T>
{
   public static long F(T t) {
      return (long)t;            // Error 
   }
}

t から int への明示的な直接変換が許可されている場合、XF(7) が 7L を返すことは容易に予想できます。ただし、標準の数値変換は、バインド時に型が数値であることがわかっている場合にのみ考慮されるため、そうではありません。

"

于 2012-11-22T08:22:15.877 に答える
0

C# で、任意の型指定された項目をジェネリック型パラメーターにキャストすることはお勧めできません。無関係なクラスがDあり、 を呼び出したとします。 のインスタンスをにUpdate<D>(new List<D>(), new A(), new C())追加しようとすると、実行時に例外が発生します。AList<D>

Oジェネリック パラメータではなく、いくつかの interface を操作する、非ジェネリックでより単純な実装を検討してください。

public interface O { }
public interface I : O { }
public abstract class A : O { }
public class C : A, I { }

public class Program {
    static void Update(List<O> l, A a, I i, C c) {
        l.Add(a);
        l.Add(i);
        l.Add(c);
    }
}
于 2012-11-22T08:04:23.997 に答える