4

与 Eric 关于不可空引用类型的博客文章类似,问题的解决方案似乎微不足道,但显然并非如此。

为什么不能在 c# 中添加结构类型?如果像下面的(坏)示例这样的东西刚刚被放入语言中,会出现什么问题?

public structural interface INode
{
 INode Next {get;}
}

class MyLinkedNode { ... } // structurally implements INode
class GraphNode { ... } // this also structurally implements INode

int CountNodes(INode start) 
{
  int i = 0;
  for (INode current = start; current != null; current = current.Next)
   i++;

  return i;
};

void GetCounts()
{
  MyLinedNode node1 = BuildLinkedNodes();
  GraphNode node2 = BuildGraphNodes();

  int count1 = CountNodes(node1);
  int count2 = CountNodes(node2);

  int count3 = CountNodes("hello world"); // compile fail, as string type structure invalid
}
4

2 回答 2

3

您将失去由虚拟调用和泛型提供的动态多态性的所有优点。

在当前版本的 C# 中CountNodes(和假设的通用版本CountNodes<T> where T : INode)被编译为单个函数,其中对Next属性的访问已在编译时固定在对象的 v-table 中的偏移量,该偏移量必须用于查找属性吸气剂。因此,可以将源自INode其他程序集并在其他程序集中定义的新类型提供给CountNodes,因为它们符合相同的 v-table 布局。

MyGraphNode由于和之间的 v-table 布局不同MyLinkedNode(它们是在不了解任何所需布局的情况下定义的),因此这种方法不起作用。编译器必须为每个实际参数类型生成新的和不同的代码。这会将您限制在编译时存在并且在调用站点已知的类型。例如,这将起作用:

GraphNode node = BuildGraphNodes();
int count = CountNodes(node);

但这不会:

object node = BuildGraphNodes();
int count = CountNodes(node);

实际上,这种能力已经被开发出来了。它被称为 C++ 模板。Microsoft 提供了一个 C++/CLI 编译器,允许您将其与 .NET 一起使用。它比您提出的“结构接口”更强大,但它很容易适应您的用例。

template<typename INode>
int CountNodes(INode^ start) 
{
    int i = 0;
    for (INode^ current = start; current != nullptr; current = current->Next)
        ++i;

    return i;
}

(C++/CLI 在 CLR 的范围内工作,不需要 DLR 支持,这与一些评论者声称的完全相反。)

鉴于 .NET 开发人员已经可以使用此功能,并且会给 C# 增加相当多的额外复杂性,因此不太可能通过 C# 设计人员的成本效益分析。因此,不要期望 C++ 模板,甚至是较小的“结构接口”成为 C# 的一部分。

如果您愿意放弃静态类型检查的所有好处,那么您现在可以在 C# 中使用后期(按名称查找,而不是 v-table 插槽)绑定。只需将参数类型更改为dynamic. 但是错误会延迟到运行时,性能也会受到重大影响。

于 2014-01-22T00:07:13.547 回答
2

为什么不能在 c# 中添加结构类型?

一般来说,这是可能的。与当前存在的相比,它需要向 C# 语言添加完全不同的分辨率形式。

如果像下面的(坏)示例这样的东西刚刚被放入语言中,会出现什么问题?

将需要一组新的规则,以及编译器中完全不同的重载方式。

话虽如此,这绝对是可能的。F#(今天)通过静态解析类型参数支持这种形式,并编译为与 C# 相同的 IL。C++/CLI 也通过模板支持这种类型的操作,模板也在编译时被解析和处理。

您今天可以通过 C# 在 C# 中完成类似的操作dynamic,尽管您会丢失所有编译时检查。

于 2014-01-22T00:09:23.613 回答