2010-06-26 87 views
1

有沒有一種方法可以在C#.net 3中投入一本字典,並且沒有所有開銷?在C#中鑄造通用詞典#

var result = new Dictionary<string, AccessoryVariant>(); 
foreach (BaseVariant variant in mVariants.Values) 
{ 
    result.Add(variant.Id, (AccessoryVariant)variant); 
} 
return result; 

我願做這樣的事情:

return (Dictionary<string, AccessoryVariant>)mVariants; 

/斯文

回答

10

假設你正在使用.NET 3.5或4,您可以在LINQ使用ToDictionary方法:

return mVariants.Values.ToDictionary(v => v.Id, v => (AccessoryVariant) v); 

或者:

return mVariants.Values.Cast<AccessoryVariant>().ToDictionary(v => v.Id); 

或(已經在使用該ID作爲關鍵假設mVariants):

return mVariants.ToDictionary(pair => pair.Key, 
           pair => (AccessoryVariant) pair.Value); 

注意,你不能直接投的字典,因爲假設mVariantDictionary<string, BaseVariant>,有人可能會添加一個非AccessoryVariant作爲一個值的字典,這將清楚地弄亂任何代碼(合理地)認爲Dictionary<string, AccessoryVariant>只包含AccessoryVariant值。

+1

+1,更快一如既往:-) – 2010-06-26 10:12:42

+0

會有性能損失,因爲ToDictionary()將始終需要遍歷原來的字典,並創建一個新的對象? – dsum 2011-01-03 17:34:42

+0

@dsum:性能比較什麼?是的,它將需要創建一個新的字典 - 但除非'mVariants'實際上*已經是一個完全正確的形式的字典,我沒有看到有其他選擇。 – 2011-01-03 20:07:56

0

由於無法將標準類型的字典添加到標準類型的字典中,因此無法將其轉換爲標準類型的字典。但是你可以施放到自己的類型的像這樣的詞典:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var list = new List<Entity<string>>() 
     { 
      new Entity<string>("1", "Some data 1"), 
      new Entity<string>("2", "Some data 2") 
     }; 

     var myCollection = (MyDictionary<string, Entity<string>>)list; 
    } 
} 

public class Entity<T> : IId<T> 
{ 
    private readonly T id; 
    private string data; 

    public Entity(T id, string data) 
    { 
     this.id = id; 
     this.data = data; 
    } 

    public T Id 
    { 
     get { return id; } 
    } 

    public string Data 
    { 
     get { return data; } 
     set { data = value; } 
    } 
} 

public interface IId<T> 
{ 
    T Id { get; } 
} 

public class MyDictionary<TKey, TValue> 
    where TValue : IId<TKey> 
{ 
    private readonly Dictionary<TKey, TValue> dictionary = new Dictionary<TKey, TValue>(); 

    public void Add(TKey key, TValue value) 
    { 
     dictionary.Add(key, value); 
    } 

    public bool Remove(TKey key) 
    { 
     var wasActuallyRemoved = dictionary.Remove(key); 

     return wasActuallyRemoved; 
    } 

    public bool TryGetValue(TKey key, out TValue value) 
    { 
     var wasSuccessfull = dictionary.TryGetValue(key, out value); 

     return wasSuccessfull; 
    } 

    public static explicit operator MyDictionary<TKey, TValue>(List<TValue> items) 
    { 
     var myDictionary = new MyDictionary<TKey, TValue>(); 

     foreach (var item in items) 
     { 
      myDictionary.Add(item.Id, item); 
     } 

     return myDictionary; 
    } 
} 

這是回答你的問題,但與LINQ使用的方法更好。

1

我已經做了一個快速的方法,通過創建一個新的字典並繞過散列計算(假設鍵保持不變)來轉換字典中的條目值。它在IL中完成,因此速度非常快。

查看http://drake7707.blogspot.com/2011/03/fast-value-operation-on-dictionary-in-c.html瞭解完整的方法細節和更多解釋。

用法爲:

pCopyDic = personsDic.CastValues<Person, PersonCopy>(p => (PersonCopy)p); 

全部方法:(你必須雖然編輯委託的緩存,我不包括,因爲它是非常容易的,不同在什麼背景下您使用它)

/// <summary> 
    /// Transforms each value in a dictionary to a new object with the given cast delegate 
    /// </summary> 
    public static Dictionary<string, To> CastValues<From, To>(this Dictionary<string, From> fromDic, Func<From, To> cast) 
    { 
    MetaData data; 
    if (!store.TryGetValue(typeof(From), out data)) 
    { 
    data = new MetaData(); 
    store.Add(typeof(From), data); 
    } 


    if (data.CastDictionaryValues == null) 
    { 
    // Create ILGenerator 
    DynamicMethod dymMethod = new DynamicMethod("DoDictionaryCastValues", typeof(Dictionary<string, To>), new Type[] { typeof(Dictionary<string, From>) }, true); 
    ConstructorInfo newDictionaryTo = typeof(Dictionary<string, To>).GetConstructor(new Type[] { typeof(int) }); 
    FieldInfo fldEntries = typeof(Dictionary<string, From>).GetField("entries", BindingFlags.NonPublic | BindingFlags.Instance); 

    FieldInfo fldEntryKey = fldEntries.FieldType.GetElementType().GetField("key", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); 
    FieldInfo fldEntryValue = fldEntries.FieldType.GetElementType().GetField("value", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); 

    ILGenerator generator = dymMethod.GetILGenerator(); 

    // define labels for loop 
    Label loopConditionCheck = generator.DefineLabel(); 
    Label insideLoop = generator.DefineLabel(); 

    // define local variables 
    generator.DeclareLocal(typeof(Dictionary<string, To>)); // toDic , 0 
    generator.DeclareLocal(fldEntries.FieldType.GetElementType()); // pair entry<string, from>, 1 
    generator.DeclareLocal(typeof(int)); // i, 2 
    generator.DeclareLocal(typeof(int)); // count, 3; 
    generator.DeclareLocal(typeof(Dictionary<string, To>).GetField("entries", BindingFlags.NonPublic | BindingFlags.Instance).FieldType.GetElementType()); // entry<to> 4; 

    // store count 
    generator.Emit(OpCodes.Ldarg_0); 
    generator.Emit(OpCodes.Callvirt, typeof(Dictionary<string, From>).GetProperty("Count").GetGetMethod()); 
    generator.Emit(OpCodes.Stloc_S, 3); 

    generator.Emit(OpCodes.Ldloc_S, 3); // load count and pass it as capacity parameter for toDic 
    generator.Emit(OpCodes.Newobj, newDictionaryTo); // toDic = new ... 
    generator.Emit(OpCodes.Stloc_0); 

    // COPY Dictionary fields to toDic 

    // toDic.buckets = fromDic.buckets; 
    generator.Emit(OpCodes.Ldloc_0); 
    generator.Emit(OpCodes.Ldarg_0); 
    generator.Emit(OpCodes.Ldfld, typeof(Dictionary<string, From>).GetField("buckets", BindingFlags.NonPublic | BindingFlags.Instance)); 
    generator.Emit(OpCodes.Stfld, typeof(Dictionary<string, To>).GetField("buckets", BindingFlags.NonPublic | BindingFlags.Instance)); 

    // toDic.comparer = fromDic.comparer; 
    generator.Emit(OpCodes.Ldloc_0); 
    generator.Emit(OpCodes.Ldarg_0); 
    generator.Emit(OpCodes.Ldfld, typeof(Dictionary<string, From>).GetField("comparer", BindingFlags.NonPublic | BindingFlags.Instance)); 
    generator.Emit(OpCodes.Stfld, typeof(Dictionary<string, To>).GetField("comparer", BindingFlags.NonPublic | BindingFlags.Instance)); 

    // toDic.count = fromDic.count; 
    generator.Emit(OpCodes.Ldloc_0); 
    generator.Emit(OpCodes.Ldarg_0); 
    generator.Emit(OpCodes.Ldfld, typeof(Dictionary<string, From>).GetField("count", BindingFlags.NonPublic | BindingFlags.Instance)); 
    generator.Emit(OpCodes.Stfld, typeof(Dictionary<string, To>).GetField("count", BindingFlags.NonPublic | BindingFlags.Instance)); 

    // toDic.freeCount = fromDic.freeCount; 
    generator.Emit(OpCodes.Ldloc_0); 
    generator.Emit(OpCodes.Ldarg_0); 
    generator.Emit(OpCodes.Ldfld, typeof(Dictionary<string, From>).GetField("freeCount", BindingFlags.NonPublic | BindingFlags.Instance)); 
    generator.Emit(OpCodes.Stfld, typeof(Dictionary<string, To>).GetField("freeCount", BindingFlags.NonPublic | BindingFlags.Instance)); 

    // toDic.freeList = fromDic.freeList; 
    generator.Emit(OpCodes.Ldloc_0); 
    generator.Emit(OpCodes.Ldarg_0); 
    generator.Emit(OpCodes.Ldfld, typeof(Dictionary<string, From>).GetField("freeList", BindingFlags.NonPublic | BindingFlags.Instance)); 
    generator.Emit(OpCodes.Stfld, typeof(Dictionary<string, To>).GetField("freeList", BindingFlags.NonPublic | BindingFlags.Instance)); 

    // toDic.entries = new Entry<,>[fromDic.entries.Length]; 
    generator.Emit(OpCodes.Ldloc_0); 
    generator.Emit(OpCodes.Ldarg_0); 
    generator.Emit(OpCodes.Ldfld, fldEntries); 
    generator.Emit(OpCodes.Ldlen); 
    generator.Emit(OpCodes.Newarr, typeof(Dictionary<string, To>).GetField("entries", BindingFlags.NonPublic | BindingFlags.Instance).FieldType.GetElementType()); 
    generator.Emit(OpCodes.Stfld, typeof(Dictionary<string, To>).GetField("entries", BindingFlags.NonPublic | BindingFlags.Instance)); 

    // End COPY 

    // i = 0; 
    generator.Emit(OpCodes.Ldc_I4_0); 
    generator.Emit(OpCodes.Stloc_2); 

    generator.Emit(OpCodes.Br, loopConditionCheck); // perform loop test 
    generator.MarkLabel(insideLoop); 
    { 
    // pair = fromDic.entries[i]; 
    generator.Emit(OpCodes.Ldarg_0); // load fromDic on stack 
    generator.Emit(OpCodes.Ldfld, fldEntries); // load entries field from dic on stack 
    generator.Emit(OpCodes.Ldloc_2); // load i 
    generator.Emit(OpCodes.Ldelem, fldEntries.FieldType.GetElementType()); // load fromDic.entries[i] 
    generator.Emit(OpCodes.Stloc_1); 

    // bypass add & insert manually into entries from toDic 

    // entryTo = new Entry<,>(); 
    generator.Emit(OpCodes.Ldloca_S, 4); 
    generator.Emit(OpCodes.Initobj, typeof(Dictionary<string, To>).GetField("entries", BindingFlags.NonPublic | BindingFlags.Instance).FieldType.GetElementType()); 

    // entryTo.key = entryFrom.key 
    generator.Emit(OpCodes.Ldloca_S, 4); 
    generator.Emit(OpCodes.Ldloc_1); 
    generator.Emit(OpCodes.Ldfld, fldEntries.FieldType.GetElementType().GetField("key", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)); 
    generator.Emit(OpCodes.Stfld, typeof(Dictionary<string, To>).GetField("entries", BindingFlags.NonPublic | BindingFlags.Instance).FieldType.GetElementType().GetField("key", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)); 

    // entryTo.hashCode = entryFrom.hashCode 
    generator.Emit(OpCodes.Ldloca_S, 4); 
    generator.Emit(OpCodes.Ldloc_1); 
    generator.Emit(OpCodes.Ldfld, fldEntries.FieldType.GetElementType().GetField("hashCode", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)); 
    generator.Emit(OpCodes.Stfld, typeof(Dictionary<string, To>).GetField("entries", BindingFlags.NonPublic | BindingFlags.Instance).FieldType.GetElementType().GetField("hashCode", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)); 

    // entryTo.next = entryFrom.next 
    generator.Emit(OpCodes.Ldloca_S, 4); 
    generator.Emit(OpCodes.Ldloc_1); 
    generator.Emit(OpCodes.Ldfld, fldEntries.FieldType.GetElementType().GetField("next", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)); 
    generator.Emit(OpCodes.Stfld, typeof(Dictionary<string, To>).GetField("entries", BindingFlags.NonPublic | BindingFlags.Instance).FieldType.GetElementType().GetField("next", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)); 

    // entryTo.value = 'to' 
    generator.Emit(OpCodes.Ldloca_S, 4); 
    // call cast(pair.value) 
    generator.Emit(OpCodes.Ldloc_1); 
    generator.Emit(OpCodes.Ldfld, fldEntryValue); // load value from pair on stack 
    generator.Emit(OpCodes.Call, cast.Method); 
    // and store the to value into the new entry 
    generator.Emit(OpCodes.Stfld, typeof(Dictionary<string, To>).GetField("entries", BindingFlags.NonPublic | BindingFlags.Instance).FieldType.GetElementType().GetField("value", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)); 

    // toDic.entries[i] = entryTo; 
    generator.Emit(OpCodes.Ldloc_0); // load entries[] 
    generator.Emit(OpCodes.Ldfld, typeof(Dictionary<string, To>).GetField("entries", BindingFlags.NonPublic | BindingFlags.Instance)); 
    generator.Emit(OpCodes.Ldloc_2); // load i 
    generator.Emit(OpCodes.Ldloc_S, 4); 
    generator.Emit(OpCodes.Stelem, typeof(Dictionary<string, To>).GetField("entries", BindingFlags.NonPublic | BindingFlags.Instance).FieldType.GetElementType()); // save element 

    generator.Emit(OpCodes.Ldc_I4_1); // load 1 
    generator.Emit(OpCodes.Ldloc_2); // load i 
    generator.Emit(OpCodes.Add); // i + 1 
    generator.Emit(OpCodes.Stloc_2); // i = i+1 
    } 
    generator.MarkLabel(loopConditionCheck); 

    generator.Emit(OpCodes.Ldloc_2); // load i 
    generator.Emit(OpCodes.Ldloc_S, 3); // load count 
    generator.Emit(OpCodes.Blt, insideLoop); // i < fromDic.entries.Length 

    generator.Emit(OpCodes.Ldloc_0); // return toDic; 
    generator.Emit(OpCodes.Ret); 

    data.CastDictionaryValues = dymMethod.CreateDelegate(typeof(Func<Dictionary<string, From>, Dictionary<string, To>>)); 
    } 
    return ((Func<Dictionary<string, From>, Dictionary<string, To>>)data.CastDictionaryValues)(fromDic); 
    }