为什么类型/定义的来源很重要?
如果你真的关心一个类型所在的命名空间,Visual Studio有多种方法可以查找,我最喜欢的两种方法如下:
- 悬停在类型/声明上方。 这通常会显示完整的类型名称。(悬停在
new SomeType()
语句上会显示方法名,这是应用于属性的内容。)
- 按F12 / 转到定义。 即使您没有定义的源代码,使用 F12 或 右键单击 -> 转到定义 也会带您进入元数据文件,显示类型的所有公共成员。这不适用于关键字(
out
, ref
, return
, null
等),但适用于基本别名类型(int
, string
等)和传统类型(enum
, interface
, class
, struct
等)。这包括命名空间、类型名称和所有公共API成员。如果有XML文档,它们也会被包含在内。如果你F12一个扩展方法,它会带你进入该扩展方法的类元数据。如果您认为某个方法是由某些东西注入的,这非常有助于确定它来自哪里。
现在并不是真正难以确定类型来自哪个命名空间了。那么使用别名“using”何时才真正需要呢?
实际场景:我一直在为XNA框架的Windows Forms模型工作。XNA框架有一个“Color”类型,而我的框架也有一个“Color”类型。现在我经常同时使用这两个命名空间,但只需要其中一个“Color”类型被本地使用。通常情况下,我的“using”语句列表包括以下内容:
using XnaColor = Microsoft.Xna.Framework.Color
using Color = Evbpc.Framework.Drawing.Color
这样可以解决一个模棱两可的问题。
为什么使用别名不是默认设置?
可能是因为它们几乎从不必要。我们并不真正需要它们。如果你担心类型来自哪个命名空间,那么进行快速查找比别名更容易,并且强制定义命名空间。在那种情况下,您可能会完全禁用using
语句并完全限定所有内容。
我曾经使用using
别名的最大两个用例是:
- Resolve ambiguity between types. See example above.
Resolve ambiguity between namespaces. Same idea as above, but I alias a whole namespace if a lot of types are duplicated.
using XnaF = Microsoft.Xna.Framework;
using Evbpc.Framework.Drawing;
如果您使用Visual Studio生成代码、导入类型等,则不会使用别名。相反,Visual Studio将根据需要完全限定名称。右键单击波浪线并得到
A.B.Type
作为唯一选项,而不是
using A.B
?那么通常这是一个很好的别名使用点。
但是我要警告您,使用别名似乎会增加可维护性要求。(可能没有支持的数字,但我不会撒谎——在我的几个别名项目中,我经常忘记如何/什么命名别名。)
通常,在我的经验中,如果您必须使用
using
别名,则
可能违反了某个规则。
为什么我们甚至不经常使用它们呢?
因为它们很糟糕。它们使代码更难阅读(以您的
DataAnnotations.MaxLength
示例为例,我为什么需要阅读它?我不在乎
MaxLength
在
System.ComponentModel.DataAnnotations
中,我只关心它是否设置正确),它们会使代码混乱(现在我
被迫记住一个属性在
System.ComponentModel.DataAnnotations
而不是
System.ComponentModel.DataAnnotations.Schema
中),而且它们通常很笨重。
以您之前的示例为例,我有一个Entity Framework项目,在类上有以下属性:
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; }
对不起,但那些只是糟糕的。这就是为什么你跟每个开发者谈论时他们都会避免使用这些并认为这是一个坏主意的原因。然后我将使用别名,而 聪明地[1] 导入命名空间并处理可能出现类型冲突的非常微小的问题。
如果你真的找不到某个类型所在的命名空间(例如你从 Stack Overflow 上获取代码),那么访问MSDN,进入Library并搜索该类型。(即搜索KeyAttribute
或MaxLengthAttribute
,第一个链接就是 API 参考文档。)
[1]: 通过“聪明”我指的是负责任、小心地这样做。不要盲目地导入/使用命名空间,尽可能地限制它们。SRP 和多态通常使我们能够在每个文件中保持 using
列表相当小。
using
语句。如果您从System.ComponentModel.DataAnnotations
命名空间中使用了 10 种类型,您是想要 10 个using
语句还是一个? - JLRisheusing DataAnnotations = System.ComponentModel.DataAnnotations;
这是关于将实际使用的内容与其using
语句绑定在一起的问题。 - BradDataAnnotations.[TypeName]
而不是仅使用[TypeName]
。这真的有益吗?我认为这只会使代码变得冗长无比。 - JLRishe