web-dev-qa-db-ja.com

C#:異なる型のジェネリッククラスのリストを宣言して使用する方法

タイプとして_string, int, float, long_のいずれかを含む次のジェネリッククラスを持っています。

_public class MyData<T>
{
    private T _data;

    public MyData (T value)
    {
        _data = value;
    }

    public T Data { get { return _data; } }
}
_

_MyData<T>_のリストを取得しようとしています。各項目は異なるTです。

次のコードのように、リストから項目にアクセスしてその値を取得できるようにしたいのですが。

_MyData<> myData = _myList[0];    // Could be <string>, <int>, ...
SomeMethod (myData.Data);
_

ここで、SomeMethod()は次のように宣言されています。

_public void SomeMethod (string value);
public void SomeMethod (int value);
public void SomeMethod (float value);
_

更新:

SomeMethod()は私が制御できない別の層クラスからのものであり、SomeMethod(object)は存在しません。


しかし、コンパイラーを幸せにする方法を見つけることができないようです。

助言がありますか?

ありがとうございました。

27
Stécy

デリゲートは、これを単純化し、タイプセーフを維持するのに役立ちます。

public void TestMethod1()
{
    Action<SomeClass, int> intInvoke = (o, data) => o.SomeMethod(data);
    Action<SomeClass, string> stringInvoke = (o, data) => o.SomeMethod(data);

    var list = new List<MyData> 
    {
        new MyData<int> { Data = 10, OnTypedInvoke = intInvoke },
        new MyData<string> { Data = "abc", OnTypedInvoke = stringInvoke }
    };

    var someClass = new SomeClass();
    foreach (var item in list)
    {
        item.OnInvoke(someClass);
    }
}

public abstract class MyData
{
    public Action<SomeClass> OnInvoke;
}

public class MyData<T> : MyData
{
    public T Data { get; set; }
    public Action<SomeClass, T> OnTypedInvoke 
    { set { OnInvoke = (o) => { value(o, Data); }; } }
}

public class SomeClass
{
    public void SomeMethod(string data)
    {
        Console.WriteLine("string: {0}", data);
    }

    public void SomeMethod(int data)
    {
        Console.WriteLine("int: {0}", data);
    }
}
8

あなたが抱えている問題は、ジェネリック型を作成してから、そのジェネリック型のリストを作成しようとしているためだと思います。たとえば、IData要素としてサポートしようとしているデータ型を縮小して、IDataの制約を使用してMyDataジェネリックを作成することで、実行しようとしていることを実現できます。これの欠点は、使用しているすべてのプリミティブデータ型(string、int、float、long)を表すために独自のデータ型を作成する必要があることです。次のようになります。

public class MyData<T, C>
    where T : IData<C>
{
    public T Data { get; private set; }

    public MyData (T value)
    {
         Data = value;
    }
}

public interface IData<T>
{
    T Data { get; set; }
    void SomeMethod();
}

//you'll need one of these for each data type you wish to support
public class MyString: IData<string>
{
   public MyString(String value)
   {
       Data = value;
   }

   public void SomeMethod()
   {
       //code here that uses _data...
       Console.WriteLine(Data);
   }

   public string Data { get; set; }
}

そして、あなたは実装が次のようなものになるでしょう:

var myData = new MyData<MyString, string>(new MyString("new string"));    
// Could be MyString, MyInt, ...
myData.Data.SomeMethod();

もう少し手間がかかりますが、目的の機能が得られます。

PDATE:インターフェイスからSomeMethodを削除し、これを実行します

SomeMethod(myData.Data.Data);
13
Joseph

ArrayListを使用して、MyData<T>タイプを忘れてください。

ArrayList myStuff = getStuff();
float x = myStuff.OfType<float>().First();
SomeMethod(x);
string s = myStuff.OfType<string>().First();
SomeMethod(s);

MyData<T>の問題は、コンパイラが実行時にのみ既知である型をチェックすることを期待していることです。コンパイラーは、コンパイル時に既知のタイプをチェックします。

6
Amy B

あなたが望む方法でそれを行うことはできません。

ジェネリッククラスのインスタンスが初期化されると、特定の型にバインドされます。リストにさまざまなタイプのオブジェクトを保持したいので、最小公分母にバインドされたインスタンスを作成する必要があります。この場合は、オブジェクトです。

ただし、これは、DataプロパティがObject型のオブジェクトを返すことを意味します。コンパイラーはコンパイル時に実際のデータ型を推測できないため、適切なSomeMethodオーバーロードを選択できます。

Objectをパラメーターとして受け取るSomeMethodのオーバーロードを提供するか、コレクションにそのような異なる型を保持するための要件を削除する必要があります。

または、標準のIEnumerableコレクション(配列など)を使用してOfType<>特定のタイプのコレクションのサブセットを取得する拡張メソッド。

3
Franci Penov

ワイルドカードの提案 ここ 。 「修正しない」としてクローズ:(

1
Kent Boogaart

SomeMethodのジェネリックラッパーを作成し、ジェネリック引数のタイプを確認してから、適切なメソッドに委任できます。

public void SomeMethod<T>(T value)
{
    Type type = typeof(T);

    if (type == typeof(int))
    {
        SomeMethod((int) (object) value); // sadly we must box it...
    }
    else if (type == typeof(float))
    {
        SomeMethod((float) (object) value);
    }
    else if (type == typeof(string))
    {
        SomeMethod((string) (object) value);
    }
    else
    {
        throw new NotSupportedException(
            "SomeMethod is not supported for objects of type " + type);
    }
}
1
Hosam Aly

その場合、MyData<object>これらのタイプに共通するのはそれだけだからです。

1
AnthonyWJones

継承MyData<T>非ジェネリックMyDataクラスから、そのリストを作成します。

この方法では、過負荷を自動的に解決できません。手動で行う必要があります。

abstract class MyData { 
   protected abstract object GetData();
   protected abstract Type GetDataType(); 
   public object Data {
      get { return GetData(); } 
   }
   public Type DataType {
      get { return GetDataType(); } 
   }
}

class MyData<T> : MyData { 
   protected override object GetData() { return Data; }
   protected override Type GetDataType() { return typeof(T); }
   public new T Data {
     get { ... }
   }
}
0
Mehrdad Afshari

ジェネリックでは、リストを作成するときにリスト全体に1つのタイプを指定できます。たとえば、intを格納するためのリストは次のように作成されます。

var myData = new MyData<int>();

同じジェネリックリストに複数の型を格納する場合は、それらの型に共通の基本型またはインターフェイスを指定できます。残念ながら、あなたの場合、格納したい型の唯一の一般的な基本型はオブジェクトです。

var myData = new MyData<object>();

ただし、オブジェクトを格納するために非ジェネリックリストを使用できます。

0
Mendelt