gpt4 book ai didi

c# - .NET 4.0 中的动态 : am I doing it right?

转载 作者:可可西里 更新时间:2023-11-01 09:09:33 25 4
gpt4 key购买 nike

昨天我使用新的 dynamic 编写了我的第一行代码输入 .NET 4.0。我发现这很有用的场景如下:

我有一个包含多个值列表的类。这可以是 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&lt;ICollection&lt;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&lt;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 动态类型的忠实拥护者,但遗憾的是对实现感到失望.因此,我对“我做得对吗”的看法归结为“这个问题是否适合动力学问题”——以下是我对此的看法。

  • 加载和 JIT 所有与动态相关的程序集的性能影响可能非常严重。
  • 第一次动态解析方法时,C-Sharp 运行时绑定(bind)器在内部抛出并捕获异常。每个调用点都会发生这种情况(即,如果您在动态对象上有 10 行代码调用方法,您会得到 10 个异常)。如果您将调试器设置为“在第一次机会异常时中断”,这真的很烦人,并且它还会用第一次机会异常消息填满调试输出窗口。您可以抑制这些,但 visual studio 这样做很烦人。
  • 这两件事加起来 - 在冷启动时,您的应用程序的加载时间可能会明显延长。在带有 SSD 的核心 i7 上,我发现当我的 WPF 应用程序首次加载所有动态内容时,它会在加载程序集、JITing 和抛出捕获异常时停止大约 1-2 秒。 (有趣的是 IronRuby 没有这些问题,它对 DLR 的实现比 C# 的实现要好得多)
  • 加载内容后,性能非常好。
  • 动力学扼杀了智能感知和 visual studio 的其他优秀功能。虽然我个人并不介意这一点,因为我有编写大量 ruby​​ 代码的背景,但我组织中的其他几位开发人员对此感到恼火。
  • 动态会使调试变得更加困难。像 ruby​​/javascript 这样的语言提供了一个 REPL(交互式提示)来帮助他们,但 C# 还没有。如果你只是使用动态来解析方法,它不会那么糟糕,但如果你尝试使用它来动态实现数据结构(ExpandoObject 等),那么调试将成为 C# 中的一个真正的痛苦。当我的同事不得不使用 ExpandoObject 调试一些代码时,他们对我更加恼火。

总的来说:

  • 如果您可以在不需要动态的情况下做某事,请不要使用它们。 C# 实现它们的方式太笨拙了,你的同事会生你的气。
  • 如果您只需要非常小的动态特征,请使用反射。使用反射引起的经常被引用的“性能问题”通常不是什么大问题。
  • 尤其要尽量避免因加载/启动性能下降而在客户端应用程序中出现动态变化。

我对这种特定情况的建议:

  • 看起来您可以通过将事物作为对象 来回传递来避免动态变化。我建议你这样做。
  • 您将不得不放弃使用 Tuple 来传递您的数据对,并制作一些自定义类,但这也可能会改进您的代码,因为您可以附加有意义的数据的名称,而不仅仅是 Item1Item2

关于c# - .NET 4.0 中的动态 : am I doing it right?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5948382/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com