![](/img/trans.png)
[英]Why is ConcurrentBag<T> so slow in .Net (4.0)? Am I doing it wrong?
[英]Dynamics in .NET 4.0: am I doing it right?
昨天我使用.NET 4.0中的新dynamic
類型編寫了我的第一行代碼。 我發現這個有用的場景如下:
我有一個類,里面有幾個值列表。 這可以是List<string>
, List<bool>
, List<int>
或任何類型的列表。 使用它們的方式是我向這些列表中的一個或多個添加值。 然后我“同步”它們,以便它們都以相同的長度結束(那些太短的都填充了默認值)。 然后我繼續添加更多值,再次同步等。目標是其中一個列表中任何索引處的項與另一個列表中相同索引處的項相關。 (是的,通過將所有這些包裝在另一個類中可能可以更好地解決這個問題,但在這種情況下,這不是重點。)
我有幾個類中的這個構造,所以我想讓列表的同步盡可能通用。 但由於列表的內部類型可能會有所不同,這並不像我最初想象的那樣直截了當。 但是,進入當天的英雄:動態:)
我編寫了以下幫助器類,它可以獲取列表(任何類型)的集合以及每個列表的默認值:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Foo.utils
{
public class ListCollectionHelper
{
/// <summary>
/// Takes a collection of lists and synchronizes them so that all of the lists are the same length (matching
/// the length of the longest list present in the parameter).
///
/// It is assumed that the dynamic type in the enumerable is of the type Tuple<ICollection<T>, T>, i.e. a
/// list of tuples where Item1 is the list itself, and Item2 is the default value (to fill the list with). In
/// each tuple, the type T must be the same for the list and the default value, but between the tuples the type
/// might vary.
/// </summary>
/// <param name="listCollection">A collection of tuples with a List<T> and a default value T</param>
/// <returns>The length of the lists after the sync (length of the longest list before the sync)</returns>
public static int SyncListLength(IEnumerable<dynamic> listCollection)
{
int maxNumberOfItems = LengthOfLongestList(listCollection);
PadListsWithDefaultValue(listCollection, maxNumberOfItems);
return maxNumberOfItems;
}
private static int LengthOfLongestList(IEnumerable<dynamic> listCollection)
{
return listCollection.Aggregate(0, (current, tuple) => Math.Max(current, tuple.Item1.Count));
}
private static void PadListsWithDefaultValue(IEnumerable<dynamic> listCollection, int maxNumberOfItems)
{
foreach (dynamic tuple in listCollection)
{
FillList(tuple.Item1, tuple.Item2, maxNumberOfItems);
}
}
private static void FillList<T>(ICollection<T> list, T fillValue, int maxNumberOfItems)
{
int itemsToAdd = maxNumberOfItems - list.Count;
for (int i = 0; i < itemsToAdd; i++)
{
list.Add(fillValue);
}
}
}
}
以下是我用來驗證我最終得到所需行為的一小部分單元測試:
using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Foo.utils;
namespace Foo.UnitTests
{
[TestClass]
public class DynamicListSync
{
private readonly List<string> stringList = new List<string>();
private readonly List<bool> boolList = new List<bool>();
private readonly List<string> stringListWithCustomDefault = new List<string>();
private readonly List<int> intList = new List<int>();
private readonly List<dynamic> listCollection = new List<dynamic>();
private const string FOO = "bar";
[TestInitialize]
public void InitTest()
{
listCollection.Add(Tuple.Create(stringList, default(String)));
listCollection.Add(Tuple.Create(boolList, default(Boolean)));
listCollection.Add(Tuple.Create(stringListWithCustomDefault, FOO));
listCollection.Add(Tuple.Create(intList, default(int)));
}
[TestMethod]
public void SyncEmptyLists()
{
Assert.AreEqual(0, ListCollectionHelper.SyncListLength(listCollection));
}
[TestMethod]
public void SyncWithOneListHavingOneItem()
{
stringList.Add("one");
Assert.AreEqual(1, ListCollectionHelper.SyncListLength(listCollection));
Assert.AreEqual("one", stringList[0]);
Assert.AreEqual(default(Boolean), boolList[0]);
Assert.AreEqual(FOO, stringListWithCustomDefault[0]);
Assert.AreEqual(default(int), intList[0]);
}
[TestMethod]
public void SyncWithAllListsHavingSomeItems()
{
stringList.Add("one");
stringList.Add("two");
stringList.Add("three");
boolList.Add(false);
boolList.Add(true);
stringListWithCustomDefault.Add("one");
Assert.AreEqual(3, ListCollectionHelper.SyncListLength(listCollection));
Assert.AreEqual("one", stringList[0]);
Assert.AreEqual("two", stringList[1]);
Assert.AreEqual("three", stringList[2]);
Assert.AreEqual(false, boolList[0]);
Assert.AreEqual(true, boolList[1]);
Assert.AreEqual(default(Boolean), boolList[2]);
Assert.AreEqual("one", stringListWithCustomDefault[0]);
Assert.AreEqual(FOO, stringListWithCustomDefault[1]);
Assert.AreEqual(FOO, stringListWithCustomDefault[2]);
Assert.AreEqual(default(int), intList[0]);
Assert.AreEqual(default(int), intList[1]);
Assert.AreEqual(default(int), intList[2]);
}
}
}
所以,既然這是我動態的第一次鏡頭(無論是在C#還是其他任何地方......),我只想問我是否正確行事。 顯然代碼按預期工作,但這是正確的方法嗎? 我有什么明顯的優化或缺陷嗎?
我已經對C#中的動態做了很多討論,我最初認為它們會非常整潔,因為我是Ruby / Javascript完成的動態類型的忠實粉絲,但在實現中卻很遺憾。 因此,我對“我是否正確行事”的看法歸結為“這個問題非常適合動態” - 這是我對此的看法。
總體上:
我對這種具體情況的建議:
Object
來避免動態。 我建議你這樣做。 Tuple
來傳遞您的數據對,並制作一些自定義類,但這可能也會改進您的代碼,因為您可以將有意義的名稱附加到數據而不僅僅是Item1
和Item2
我相信動態關鍵字主要是為了使Microsoft Office interop更容易添加,以前你必須編寫非常復雜的代碼(在C#中)才能使用Microsoft Office API,Office界面代碼現在可以更加清晰。
對此的共鳴是Office API最初編寫為由Visual Basic 6(或VB腳本)使用; .NET 4.0添加了幾種語言功能,以使其更容易(以及動態,也可以獲得命名和可選參數)。
當您使用dynamic關鍵字時,它會丟失編譯時檢查,因為使用dynamic關鍵字的對象在運行時被解析。 由於必須加載提供動態支持的程序集,因此存在一些內存開銷。此外,還會有一些性能開銷,類似於使用Reflection。
我不認為這是動態的解決方案。 當您需要有條件地處理一堆不同類型時,動態非常有用。 如果這是一個字符串,做一些事情,如果它是一個int,做其他事情,如果它是一個Puppy類實例,則調用bark()。 動態釋放你不必像這樣亂丟垃圾代碼與大量的類型鑄造或丑陋的泛型。 動態和其他高級語言功能的用途適用於代碼生成器,解釋器等......
這是一個很酷的功能,但除非你使用動態語言或COM互操作,否則它只適用於你有一個討厭的高級問題。
我認為你可以通過使用IList
來完成同樣的事情,而不是在這里使用動態。 (非泛型)兩者都消除了編譯時類型檢查,但由於泛型列表也實現了IList
,您仍然可以使用IList
運行時類型檢查。
另外,側面問題,為什么你使用.Aggregate()
而不是.Max()
來查找最大長度?
我還沒有密切關注它,但在聲明集合時,是否真的需要使用動態關鍵字? 在.NET 4.0中,還有支持協方差和逆變的新機制 ,這意味着您還應該能夠使用下面的代碼。
var listCollection = new List<IEnumerable<object>>();
listCollection.Add(new List<int>());
這里的缺點是你的列表只包含IEnumerable實例,而不是可以直接修改的東西,如果你的實現需要的話。
除了這個考慮之外,我認為動態的使用很好,因為你正在使用它們,但你確實犧牲了C#通常提供的許多安全機制。 所以我建議如果你使用這種技術我會建議在一個包含良好且經過測試的類中編寫它,它不會將動態類型暴露給任何更大的客戶端代碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.