【问题标题】:How to alias a built-in type in C#?如何在 C# 中为内置类型设置别名?
【发布时间】:2010-07-15 19:34:42
【问题描述】:

所以在 C++ 中,我已经习惯了能够做到:

typedef int PeerId;

这使我可以使类型更具自记录性,而且还允许我在不更改所有代码的情况下随时使 PeerId 表示不同的类型。如果我愿意,我什至可以把 PeerId 变成一个类。这种可扩展性是我希望在 C# 中拥有的,但是我无法弄清楚如何在 C# 中为“int”创建别名。

我想我可以使用 using 语句,但我相信它只在当前文件中具有作用域,所以这不起作用(别名需要在多个文件之间可访问而无需重新定义)。我也不能从内置类型派生一个类(但通常这是我对引用类型的别名所做的,例如 List 或 Dictionary)。我不确定我能做什么。有什么想法吗?

【问题讨论】:

  • 为什么不能从内置类型派生?
  • 我只是好奇?有人想要这样做的情况或原因是什么?对不起,如果这听起来很奇怪,我只是不知道你为什么不想使用 int。听起来其他开发人员阅读您的代码会感到困惑。
  • 同意,有兴趣阅读这篇文章,但一定是不好的做法
  • 不是不好的做法。当 [a] 数据类型可能改变 [b] 你想隐藏实现细节时使用它(因为它们可能会改变)。
  • 在这里寻找指导,我相信给内置类型起别名对于强类型检查很有用。例如,假设我有一个程序将毫秒作为一个 int 字段,将秒作为另一个 int 字段。为毫秒和秒创建 typedef 是确保在没有转换的情况下在两个“类型”之间不发生比较或分配的一种方法。处理这些情况的以 C# 为中心的方法是什么?

标签: c# alias


【解决方案1】:

您需要像这样使用完整的类型名称:

using DWORD = System.Int32;

【讨论】:

  • 这个解决方案没有处理他的这部分问题:'我想我可以使用 using 语句,但它只在我相信的当前文件中具有范围,所以这不起作用(别名需要在多个文件之间可访问而不被重新定义)'
【解决方案2】:

你可以(ab)使用隐式转换:

struct PeerId
{
    private int peer;

    public static implicit operator PeerId(int i)
    {
        return new PeerId {peer=i};
    }

    public static implicit operator int(PeerId p)
    {
        return p.peer;
    }
}

这与 int 占用相同的空间,您可以这样做:

PeerId p = 3;
int i = p;

但我同意你可能不需要这个。

【讨论】:

  • 我有时会为稍微复杂一点的值类型这样做,但我不会仅仅为了一个名字而这样做。
  • 您还可以将 int 转换为 PeerId 显式转换。通过强制积分器有意识地转换为包装类型,它将在某些情况下提供更大的价值。这对于将物理单位作为参数的函数(例如,以英尺与米计算)等功能特别有用,可防止某些类型的琐碎错误。
【解决方案3】:

总结

以下是简短的回答:

  • Typedefs 实际上是编译时代码生成器使用的变量。
  • C# 旨在避免添加代码生成语言结构。

因此,typedef 的概念并不适合 C# 语言。

长答案

在 C++ 中,它更有意义:C++ 最初是一个预编译器,它会生成 C 代码,然后再进行编译。这种“代码生成器”的开端仍然对现代 C++ 特性产生影响(即,模板本质上是一种图灵完备的语言,用于在编译时生成类和函数)。在这种情况下,typedef 是有意义的,因为它是一种获取编译时类型工厂的“结果”或“返回”类型的“算法”的方法。

在这种奇怪的元语言(Boost 之外很少有人掌握)中,typedef 实际上是一个变量。

您所描述的内容不那么复杂,但您仍在尝试将 typedef 用作变量。在这种情况下,它被用作输入变量。因此,当其他代码使用 typedef 时,实际上并没有直接使用该类型。相反,它充当编译时代码生成器,基于 typedef 的输入变量构建类和方法。即使忽略 C++ 模板,只看 C typedef,效果也是一样的。

C++ 和生成式编程

C++ 被设计为一种多范式语言(OO 和过程,但在 Boost 出现之前无法使用)。有趣的是,模板已经演变出一种意想不到的范式:生成式编程。 (生成式编程在 C++ 之前就已经存在,但是 C++ 使它流行起来)。生成程序实际上是元程序,在编译时会生成所需的类和方法,然后将其编译成可执行文件。

C# 和生成式编程

我们的工具正朝着同一个方向缓慢发展。当然,反射发射可以用于“手动”生成式编程,但它是相当痛苦的。 LINQ 提供程序使用表达式树的方式本质上是非常有生成性的。 T4 模板非常接近,但仍然不足。 “编译器即服务”有望成为 C# vNext 的一部分,如果它可以与某种类型变量(例如 typedef)相结合,它似乎是最有希望的。

其中一个难题是still missing:生成程序需要某种自动触发机制(在 C++ 中,这是由隐式模板实例化处理的)。

然而,在 C# 语言中拥有任何类型的“代码生成器”(如 C++ 模板)显然不是 C# 的目标(可能是为了便于理解;很少有 C++ 程序员了解 C++模板)。这可能是由 T4 而不是 C# 满足的利基市场。

结论(重复总结)

以上都是这么说的:

  • Typedef 是代码生成器使用的变量。
  • C# 旨在避免添加代码生成语言结构。

因此,typedef 的概念并不适合 C# 语言。

【讨论】:

  • 非常丰富的回答,但它并不包含我不知道的任何信息,也没有真正回答我最初的问题。我只是提到 typedefs 给每个人一个我在想/期待的例子。别名不一定需要在编译时,并且可以采用多种形式,例如派生。如果我能做到这一点:“public class PeerId : int {}”,这就足够了,并且完美地解决了我的问题。
  • 哈哈,C# Source Generators 去 brrr
【解决方案4】:

我有时也觉得我需要(整数)类型定义用于与 OP 类似的目的。

如果您不介意演员表是明确的(我实际上希望他们是),您可以这样做:

enum PeerId : int {};

也适用于byte, sbyte, short, ushort, uint, long,ulong(显然)。

不完全是 enum 的预期用途,但它确实有效。

【讨论】:

    【解决方案5】:

    从 C# 10 开始,您可以使用 global using

    global using PeerId = System.Int32;
    

    它适用于所有文件。

    它应该出现在所有不带全局修饰符的 using 指令之前。

    using directive

    【讨论】:

    • 我同意,这是版本 10 中更理想的答案。我已将标记答案更改为这个。
    【解决方案6】:

    仅仅为了改变名称而重新定义基本类型是 C++ 的想法,并且不适合更纯粹的面向对象的 C#。每当您有将一个概念从一种语言硬塞到另一种语言的冲动时,您必须停下来思考它是否有意义,并尝试保持对平台的本地化。

    可以通过定义自己的值类型来满足能够轻松更改基础类型的要求。结合隐式转换运算符和算术运算符,您可以定义非常强大的类型。如果您担心在简单类型之上添加层的性能,请不要。 99% 的可能性不会,1% 的可能性是万一发生了,它不会是性能优化的“唾手可得的果实”。

    【讨论】:

      猜你喜欢
      • 2012-02-01
      • 2011-08-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-11
      相关资源
      最近更新 更多