为什么默认不使用 C# "using aliases"?

Why C# "using aliases" are not used by default?

考虑以下代码。

using System.ComponentModel.DataAnnotations;

namespace Foo
{
    public class Bar
    {
        [Required, MaxLength(250)]
        public virtual string Name { get; set; }
    }
}

除非你有一个奇特的 IDE(即在幕后进行各种查找和静态分析),否则 "Required" 和 "MaxLength" 的实际来源是相当模糊的。特别是当可能导入多个命名空间时,具有相似的含义。

作为 C# 的新手,我发现自己总是很难弄清楚某些东西的来源。尤其是在 Whosebug 等地方查看其他代码片段时。

using DataAnnotations = System.ComponentModel.DataAnnotations;

namespace Foo
{
    public class Bar
    {
        [DataAnnotations.Required, DataAnnotations.MaxLength(250)]
        public virtual string Name { get; set; }
    }
}

现在很明显 "Required" 和 "MaxLength" 的来源。你可以更进一步,做类似的事情:

using Required = System.ComponentModel.DataAnnotations.RequiredAttribute;
using MaxLength = System.ComponentModel.DataAnnotations.MaxLengthAttribute;

namespace Foo
{
    public class Bar
    {
        [Required, MaxLength(250)]
        public virtual string Name { get; set; }
    }
}

现在这与 PHP 和 Js ES6 的工作方式非常相似。

我很好奇为什么这不是 C# 的默认设置?为什么几乎所有与我交谈过的其他 C# 开发人员都认为别名的不良做法?可能有一些潜在的性能原因吗?

为什么 types/definitions 来自哪里很重要?

如果您真的很关心某个类型存在于哪个命名空间中,Visual Studio 可以通过 几种 方式找到答案,这两种方式是我最喜欢的如下:

  • 将鼠标悬停在类型/声明上。 这通常会显示完整的类型名称。 (将鼠标悬停在 new SomeType() 语句上会显示方法名称,这是应用于属性的名称。)
  • 按 F12/转到定义。 即使您没有定义的来源,也可以使用 F12右键单击 -> 转到定义 将带您到显示该类型的所有 public 成员的元数据文件。这不适用于关键字(outrefreturnnull 等),但它适用于基本别名类型(intstring,等等)和传统类型(enuminterfaceclassstruct,等等)。这包括名称空间、类型名称和所有 public API 成员。如果有 XML 文档,它们也包括在内。如果您 F12 一个扩展方法,它会将您带到 class 元数据 该扩展方法 。这是非常有助于识别方法的来源,如果你觉得它是由不应该的东西注入的。

所以现在 真的 并不难确定该类型来自哪个命名空间。那么 using 别名呢,我们什么时候 真正 需要它们?

现实生活场景:我一直在为 XNA Framework 开发 Windows Forms 模型。 XNA Framework 有一个 Color 类型,而我的框架有一个 Color 类型。现在我经常同时使用这两个命名空间,但只需要 Color 类型中的一个 本机使用。很多时候我有一个 using 语句列表,其中包括如下内容:

using XnaColor = Microsoft.Xna.Framework.Color;
using Color = Evbpc.Framework.Drawing.Color;

所以这解决了一个歧义问题。

为什么 using 别名不是默认值?

可能是因为它们几乎从 不需要。我们真的不需要它们。如果您担心类型来自哪个命名空间,那么 far 进行快速查找比给所有内容加上别名和 force 命名空间更容易被定义为。按照这个速度,您也可以完全禁止 using 语句并完全限定所有内容。

曾经 遇到过的 using 别名的最大两个用例是:

  1. 解决类型之间的歧义。参见上面的示例。
  2. 解决命名空间之间的歧义。同上的想法,但是如果很多类型重复,我给整个命名空间起别名。

    using XnaF = Microsoft.Xna.Framework;
    using Evbpc.Framework.Drawing;
    

如果您使用 Visual Studio 生成代码、导入类型等,不会 使用别名。 Visual Studio 将根据需要完全限定名称。是否曾经右键单击波浪线并获得 A.B.Type 作为唯一选项而不是 using A.B嗯,这通常是别名的好地方。

不过我要警告你,using 别名似乎增加了可维护性要求。 (这可能没有备份数字,但我不会说谎 - 我在这个项目中有几个别名导致我忘记了 how/what 我经常给别名命名 。)

一般来说,根据我的经验,如果您必须使用 using 别名,那么您 可能 违反了某处规则。

为什么我们甚至 不定期使用它们?

因为他们很烂。它们使代码更难阅读(以 DataAnnotations.MaxLength 为例,我为什么需要阅读它?我不关心 MaxLengthSystem.ComponentModel.DataAnnotations 中,我只关心它的设置正确地),它们使代码混乱(现在我被迫记住一个属性在System.ComponentModel.DataAnnotations而不是System.ComponentModel.DataAnnotations.Schema),而且它们通常很笨重。

以你之前的例子为例,我有一个 Entity Framework 项目,它在 class 上具有如下属性:

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

[Key, Column(Order = 2)]
[MaxLength(128)]
public string UserId { get; set; }

[ForeignKey(nameof(UserId))]
public virtual ApplicationUser User { get; set; }

现在用你的例子我会得到以下之一:

using DataAnnotations = System.ComponentModel.DataAnnotations;

[DataAnnotations.Key, DataAnnotations.Schema.Column(Order = 2)]
[DataAnnotations.MaxLength(128)]
public string UserId { get; set; }

[DataAnnotations.Schema.ForeignKey(nameof(UserId))]
public virtual ApplicationUser User { get; set; }

或者:

using DataAnnotations = System.ComponentModel.DataAnnotations;
using Schema = System.ComponentModel.DataAnnotations.Schema;

[DataAnnotations.Key, Schema.Column(Order = 2)]
[DataAnnotations.MaxLength(128)]
public string UserId { get; set; }

[Schema.ForeignKey(nameof(UserId))]
public virtual ApplicationUser User { get; set; }

或者更糟:

using KeyAttribute = System.ComponentModel.DataAnnotations.KeyAttribute;
using MaxLengthAttribute = System.ComponentModel.DataAnnotations.MaxLengthAttribute;
using ColumnAttribute = System.ComponentModel.DataAnnotations.Schema.ColumnAttribute;
using ForeignKeyAttribute = System.ComponentModel.DataAnnotations.Schema.ForeignKeyAttribute;

[Key, Column(Order = 2)]
[MaxLength(128)]
public string UserId { get; set; }

[ForeignKey(nameof(UserId))]
public virtual ApplicationUser User { get; set; }

对不起,那些太糟糕了。这就是为什么 'every' 与您交谈的开发人员会避开它们并认为这是个坏主意。我将坚持智能地[1]导入名称空间并处理非常分钟的潜力类型冲突。 我就用个别名吧

如果您真的找不到类型所在的名称空间(假设您从 Stack Overflow 中提取代码),则点击 MSDN, go to the Library 并搜索该类型。 (即,搜索 KeyAttributeMaxLengthAttribute,第一个链接是 API 参考。)

[1]:聪明地我的意思是带着责任和谨慎去做。不要只是 盲目地 导入/使用名称空间,尽量限制它们。 SRP 和多态性通常允许我们在每个文件中保持 using 列表非常小。