4

キーが型である辞書のような構造を作成する非常に簡単なトリックがあります。この構造はDictionary<Type, T?>、キーがTypeオブジェクトで、値が対応する型のインスタンスである のように機能します。

この素晴らしい構造は、コンパイラ/JITter によって「検索」が 1 回だけ行われ、適切な値の参照がプログラムにコンパイルされるため、単なる変数や配列と同じくらい高速です。

    public static class MyDict<T> {
        public static T Value { get; set; }
    }

次のようにその構造を操作できます。

MyDict<string>.Value = MyDict<int>.Value.ToString();

問題は、この「辞書」がグローバルであることです。異なる辞書を作成する唯一の方法は、異なるクラスを作成することです。

同様の(最速の「ルックアップ」、ボクシングなし)非静的構造を作成するにはどうすればよいですか? (コード生成なし)

簡単に言えばDictionary<Type, object>、検索コスト、キャスト、およびボクシングなしで、複数のようなオブジェクトが必要です。

4

8 に答える 8

2

質問で説明されている方法を拡張するアプローチは次のとおりです。

public class TypeDict
{
    public T Get<T>()
    {
        return MyDict<T>.Values[this];
    }
    public void Set<T>(T value)
    {
        MyDict<T>.Values[this] = value;
    }
    private static class MyDict<T>
    {
        public static Dictionary<TypeDict, T> Values { get; private set; }

        static MyDict()
        {
            Values = new Dictionary<TypeDict, T>();
        }
    }
}

これで、次のように TypeDict を使用できます。

void X()
{
    var a = new TypeDict();
    var b = new TypeDict();

    a.Set<int>(1);
    a.Set<double>(3.14);
    a.Set("Hello, world!");

    //Note that type inference allows us to omit the type argument
    b.Set(10);          
    b.Set(31.4);  
    b.Set("Hello, world, times ten!");

    Console.WriteLine(a.Get<int>());
    Console.WriteLine(a.Get<double>());
    Console.WriteLine(a.Get<string>());

    Console.WriteLine();
    Console.WriteLine(b.Get<int>());
    Console.WriteLine(b.Get<double>());
    Console.WriteLine(b.Get<string>());
}
于 2012-12-28T04:10:34.640 に答える
1

@Konstantinの回答により、実際には非常に高速なルックアップメソッド(配列のインデックス付け)があることを思い出しました。この大まかなPoCコードは、必要な構造の変形を示しています。

    public class TypeDictionary {
        static int _maxId = 0;
        int _id;

        static class Store<T>{
            internal static List<T> Values = new List<T>();
        }

        public TypeDictionary() {
            _id = _maxId++;
        }

        public T GetValue<T>() {
            return Store<T>.Values[_id];
        }

        public void SetValue<T>(T value) {
            while(Store<T>.Values.Count < _id) {
                Store<T>.Values.Add(default(T));
            }
            Store<T>.Values[_id] = value;
        } 
    }

このコードは次のように使用できます。

        var dict1 = new TypeDictionary();
        dict1.SetValue("my string");
        string result = dict1.GetValue<string>();

このソリューションの問題は、リポジトリがスパースでないために発生するメモリ使用量です。これはまた、初回の値設定をより高価にします。

于 2012-12-28T03:48:14.620 に答える
1

より複雑なバージョン。それが近いかどうかわからない:

一般的な辞書を定義します。

public class MyDictionary<T>
{
    Dictionary<string, T> dict;

    public MyDictionary()
    {
        dict = new Dictionary<string, T>();
    }

    public T this[string name]
    {
        get
        {
            if (dict.ContainsKey(name))
                return dict[name];
            else
                return default(T);//or throw
        }
        set
        {
            dict[name] = value;
        }
    }
}

次に、それらの辞書を格納するリポジトリ:

public class MyRepository
{
    List<object> repo;

    public MyRepository()
    {
        repo = new List<object>();
    }

    public void Add<T>(string name, T value)
    {
        if (!repo.OfType<MyDictionary<T>>().Any())
            repo.Add(new MyDictionary<T>());
        var dict = repo.OfType<MyDictionary<T>>().FirstOrDefault();
        dict[name] = value;
    }

    public T GetValue<T>(string name)
    {
        if (!repo.OfType<MyDictionary<T>>().Any())
            return default(T);//or throw
        else
        {
            var dict = repo.OfType<MyDictionary<T>>().FirstOrDefault();
            return dict[name];
        }
    }
}

最後に、このリポジトリを使用できます。

        MyRepository repo = new MyRepository();
        repo.Add("A", 1);
        repo.Add("B", 1);
        int i = repo.GetValue<int>("A") + repo.GetValue<int>("B");

この例では、左MyDictionary<T>にボクシングがあります。object

反対に、特定のタイプを使用している場合は、リポジトリ クラスをまったく使用しない場合があります。ただし、個別の辞書を使用してください。

MyDictionary<int> intDict = new MyDictionary<int>();
intDict["A"] = 1;
intDict["B"] = 2;
int i = intDict["A"] + intDict["B"];

ただし、それはで作業するのと同じです

Dictionary<string, int> intDict = new Dictionary<string, int>();

そのため、MyRepositoryクラスを編集してDictionary<string, T>の代わりに使用することができますMyDictionary<T>

于 2012-12-28T03:18:23.097 に答える
1

アークくんはジェネリクスを使って、基本的にコンパイル時にユニークな型を生成しています。ジェネリック型では、静的メンバーはその特定のクローズド ジェネリック型に固有です。このようにして、標準の静的メンバー ルックアップと同じくらい高速に処理されます。

上記の使用法は、次のようなものと同等です。

public static class MyDict_String 
{
    public static string Value { get; set; }
}

public static class MyDict_Int32
{
    public static int Value { get; set; }
}

MyDict_String.Value = MyDict_Int32.Value.ToString();

私の知る限り、タイプは「静的」です(そのように複数を定義することはできません)ので、これを回避して静的にコンパイルされたメンバールックアップと同じパフォーマンスを維持する方法がわかりません。

それ以外の場合の最善の策は(私が思うに) 、値を挿入/取得するときにボクシング/キャストを実行する必要があるSystem.TypeキーとSystem.Object値を使用する独自の辞書をラップするジェネリックインスタンスタイプを作成することです。

編集:これは辞書をラップする簡単な実装です:

public class MyTypedDict
{
    private Dictionary<Type, object> Values = new Dictionary<Type, object>();

    public T Get<T>()
    {
        object untypedValue;
        if (Values.TryGetValue(typeof(T), out untypedValue))
            return (T)untypedValue;
        return default(T);
    }

    public void Set<T>(T value)
    {
        Values[typeof(T)] = value;
    }
}

もっと考えてみると、ExpandoObject( http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject.aspx ) を使用して、よりプロパティに似た構文を達成することは可能かもしれませんが、私はこれはかなり乱暴で、実行時エラーがひどく発生しやすいとしか思えません。(さらに、コンパイル時に何も提供しません)

EDITx2: 本当に異なる値のセットが必要な場合は、別のジェネリック型内にネストできます。

public static class ValueSets<T>
{
    public static class MyDict<U>
    {
        public static U Value { get; set; }
    }
}

次のような使い方で:

ValueSets<int>.MyDict<string>.Value = "Hello ";
ValueSets<bool>.MyDict<string>.Value = "World!";

string helloworld = ValueSets<int>.MyDict<string>.Value + ValueSets<bool>.MyDict<string>.Value;
Console.WriteLine(helloworld);//Hello World!

しかし、その後、最初のタイプintboolこの場合は「魔法」になり、意味がなくなります。さらに、使用したい値の個別のセットごとに一意のタイプを提供する必要があります。さらに、それをインスタンス変数として渡したり変更したりすることはできず、むしろ静的にアクセスできます ( type を使用するアクセス権がある限りT)。したがって、おそらく、意味のある名前が付けられた最小限の可視型を宣言し、それらを使用できます。

internal class MyFirstWords {}
internal class MySecondWords {}

ValueSets<MyFirstWords>.MyDict<string>.Value = "Hello ";
ValueSets<MySecondWords>.MyDict<string>.Value = "World!";

string helloworld = ValueSets<MyFirstWords>.MyDict<string>.Value + ValueSets<MySecondWords>.MyDict<string>.Value;
Console.WriteLine(helloworld);//Hello World!

とにかく、これは非常に風変わりであり、お勧めしません。

于 2012-12-28T02:59:42.723 に答える
0

これを試して:

public class MyDictionary
{
    List<object> values;

    public MyDictionary()
    {
        values = new List<object>();
    }

    public T GetValue<T>()
    {
        return values.OfType<T>().FirstOrDefault();
    }

    public bool Add<T>(T value)
    {
        if (values.OfType<T>().Any())
            return false;
        else
        {
            values.Add(value);
            return true;
        }
    }
}

そしてそれを使用します:

var md = new MyDictionary();
md.Add("!!!");
string s = md.GetValue<string>();

このクラスは、最大1つのタイプの値を格納できますT。しかし、私が推測する派生クラスとインターフェースでは、コーナーケースが発生する可能性があります。ニーズに合っているかどうかを確認し、一般的に必要なものに近い場合は、必要に応じて変更することもできます。

于 2012-12-28T03:05:41.430 に答える
0

ConditionalWeakTable<TKey,TValue>あなたが説明している使用例は、.NET 4.0 に追加された目的と非常に密接に適合しています。説明する目的のために、そのようなテーブルを静的ジェネリッククラスに含め、特定のタイプのアイテムへの参照を含むと想定されるすべてのクラスオブジェクトについて、そのタイプのテーブルに想定されるオブジェクトへの参照を格納しますアイテムへの参照、または単純なアイテムホルダーオブジェクトへの参照のいずれかとともにアイテムを含めるため (ConditionalWeakTableオブジェクトが存在しなくなると、エントリは蒸発しますが、それ以外の場合は不変であることに注意してください。したがって、変更可能な関連付けが必要な場合それを保持するオブジェクトを作成する必要があります)。

于 2013-12-10T17:57:14.713 に答える
0

あなたが探しているものは、C# では不可能です。この言語は、異なるタイプの複数のオブジェクトを格納できるコンテナーをサポートしていませんが、キャスト、ボックス化、またはボックス化解除を伴わないルックアップ メソッドを提供します。C++ のマクロを使用するか、実行時に型の構造を変更できる javascript などの言語を使用して、このようなことを実現できます。

于 2013-01-03T06:16:21.137 に答える