把对象转换为dynamic类型以调用正确的重载方法是一个好的做法吗?

20
我的问题是关于在C# 4中是否适当地使用dynamic关键字。
我有一些辅助方法,它们提供了比标准的ToString方法更有用的对象表示形式,我在单元测试中使用。这里是一个简化的例子:
public static string PrettyPrint<T>(IEnumerable<T> list)
{
    return string.Join(", ", list);
}

// Needed because string is IEnumerable<char>, to prevent
// "Hello" -> "H, e, l, l, o"
public static string PrettyPrint(string s)
{
    return s;
}

public static string PrettyPrint(object o)
{
    return o.ToString();
}

我使用它们类似于这样:

public static void PrettyPrinting()
{
    object[] things = { 1, "Hello", new int[] {1, 2, 3} };

    foreach (dynamic item in things)
    {
        Console.WriteLine(PrettyPrint(item));
    }
}
这会生成以下输出:
1
Hello
1, 2, 3

注意,如果我将dynamic关键字替换为object,则会得到以下结果(所有调用都通过PrettyPrint(object)路由),这正是我想要避免的:

1
Hello
System.Int32[]

那么我的问题本质上是,这是一种代码异味还是将一个object强制转换为dynamic的方法是合法的?


7
+1 酷炫的类型系统案例! - ewernli
这有什么替代方案吗?foreach 循环是最耗费性能的循环之一。我很想看看 CL 是如何创建该代码以及在实际应用环境中它的效率如何。 - Ryan Ternier
3个回答

6
只要不滥用它,像这样的鸭子类型是为什么将dynamic添加到语言中的一部分原因。

4
@errorstacks - var 只适用于静态类型变量吗?我很确定使用 var 声明的任何变量其类型在编译时已知。 - Adam Rackis
6
"var" 和 "dynamic" 是完全不同的关键词。@errorstacks 的错误是认为它们是相同的。 - BoltClock
如果我可以给蜂窝加点糖果的话,var 用于当你知道类型但是懒得将其输入的时候 ;P Dynamic 则在运行时使用,因为你不知道类型。你可以构建一个工厂或者它周围的子类来提供更强的基础,但现在这是快速开发与流程/结构的争论。 - Ryan Ternier
@Ryan “但是太懒得打出来了 ;P” - 完全同意,但在投影匿名类型时使用它也是必要的 :-) - Adam Rackis
@Adam 哈哈。我理解 var 关键字的必要性,但我也审查过无数由喜欢滥用 var 的初级开发人员编写的代码... - Ryan Ternier
@Ryan - 无数个代码片段都是由喜欢滥用变量的初级开发人员编写的...是啊,资深开发人员也一样。这个愚蠢的关键字对许多人来说就像海妖之歌一样诱人。 - Adam Rackis

4
关于你的问题,我不是100%确定,因为我不知道你们团队的编码风格。(我也看到了很少的注释 ;))
尽管DuckTyping有它的用处,但开发者在使用之前需要知道他们在做什么。否则就像拿着剪刀跑步一样,可能会像C#系统中的其他关键字一样被滥用。
个人而言,我更愿意看到扩展方法,但根据开发者、他/她的参数和所做的文档,我可能会允许它的使用。
我犹豫的最大原因(这个例子相对柔和,与我在网上看到的一些例子相比)是它阻止了您在编译时发现问题。它需要更多的QA测试,更多的边界测试,并且有更高的失败风险。

1
哇 - 显然有人喜欢对那些没有完全赞同动态类型的答案进行投票。仅仅因为它是新的和时髦的,并不意味着它的所有方面都是惊人的。 - Ryan Ternier

3

虽然不是对确切问题的回答,但我认为你的代码不太面向对象,这也是另一种坏味道。

理想情况下,你希望调用item.PrettyPrint(),每个项目都应该返回其表示形式,并覆盖PrettyPrint

幸运的是,现有类型可以使用扩展方法进行扩展。它们使您能够添加方法,这就是我要做的事情。

如果您仍然希望将每种类型的显示逻辑放在一个类中,我建议将扩展方法与访问者模式结合使用。

话虽如此,我没有C#环境,因此无法测试我提出的解决方案。如果您尝试了这个解决方案,请告诉我它是否有效。


2
嗯...关于访问者+扩展方法,不太确定,但我认为这可能会导致扩展内部的代码像这样:... else if (item is string) { visitor.Visit((string)item); } else if (item is IEnumerable) { visitor.Visit((IEnumerable)item) } - Alexander Mavrinsky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接