在.NET中,属性是什么?

210

.NET中的属性是什么?它们有什么作用?我该如何创建自己的属性?

11个回答

148

元数据。关于你的对象/方法/属性的数据。

例如,我可以声明一个名为:DisplayOrder 的特性(attribute),这样我就可以轻松控制属性在 UI 中应该以什么顺序显示。然后,我可以将其附加到一个类,并编写一些 GUI 组件来提取属性并适当地排序 UI 元素。

public class DisplayWrapper
{
    private UnderlyingClass underlyingObject;

    public DisplayWrapper(UnderlyingClass u)
    {
        underlyingObject = u;
    }

    [DisplayOrder(1)]
    public int SomeInt
    {
        get
        {
            return underlyingObject .SomeInt;
        }
    }

    [DisplayOrder(2)]
    public DateTime SomeDate
    {
        get
        {
            return underlyingObject .SomeDate;
        }
    }
}

因此,当使用我的自定义GUI组件时,确保SomeInt始终在SomeDate之前显示。

然而,你会发现它们最常用于直接编码环境之外。例如,Windows Designer广泛使用它们以便知道如何处理定制的对象。可以这样使用BrowsableAttribute:

[Browsable(false)]
public SomeCustomType DontShowThisInTheDesigner
{
    get{/*do something*/}
}

告诉设计人员在设计时不要在属性窗口中列出此属性,例如。

您也可以将它们用于代码生成、预编译操作(例如Post-Sharp)或运行时操作,如Reflection.Emit。例如,您可以编写一些代码来分析性能,对代码进行透明的封装,对每个调用所需的时间进行计时。您可以通过在特定方法上放置属性来“退出”计时。

public void SomeProfilingMethod(MethodInfo targetMethod, object target, params object[] args)
{
    bool time = true;
    foreach (Attribute a in target.GetCustomAttributes())
    {
        if (a.GetType() is NoTimingAttribute)
        {
            time = false;
            break;
        }
    }
    if (time)
    {
        StopWatch stopWatch = new StopWatch();
        stopWatch.Start();
        targetMethod.Invoke(target, args);
        stopWatch.Stop();
        HandleTimingOutput(targetMethod, stopWatch.Duration);
    }
    else
    {
        targetMethod.Invoke(target, args);
    }
}

声明它们很容易,只需创建一个继承自Attribute的类即可。

public class DisplayOrderAttribute : Attribute
{
    private int order;

    public DisplayOrderAttribute(int order)
    {
        this.order = order;
    }

    public int Order
    {
        get { return order; }
    }
}

记住,当使用属性时,可以省略后缀“attribute”,编译器会自动添加。

注意:属性本身不起任何作用 - 需要有其他代码使用它们。有时候,这些代码已经为您编写好了,但有时候您需要自己编写。例如,C#编译器关心某些属性,某些框架使用一些属性(例如,NUnit在加载程序集时查找类上的[TestFixture] 和测试方法上的 [Test])。
因此,在创建自己的自定义属性时,请注意它不会对代码行为产生任何影响。您需要编写使用反射检查属性并对其进行操作的其他部分。


32
以下是.NET框架中所有(内置的)属性列表链接:http://msdn.microsoft.com/en-us/library/aa311259(VS.71).aspx。 - wprl
1
你会如何将你的“SomeProfilingMethod”用作属性? - RayLoveless
@RayLoveless 这不是一个属性,SomeProfilingMethod 是寻找分析属性的仪器代码。特别是在我给出的例子中,它正在寻找一个“opt-out”属性(NoTimingAttribute),而不是一个“opt-in”属性。这个想法是对所有东西进行计时。 - Quibblesome
@Quibblesome,你能否添加一些类似于“属性本身并不起作用 - 需要有一些其他代码来使用它们(编译器关心一对,不同的框架使用一些)。仅创建属性不会影响代码的行为 - 您需要编写检查属性(通过反射)并对其进行操作的其他部分。”(如果您同意,我可以这样做)。许多人期望属性神奇地工作,但这里的回答都没有澄清这一点。(或者只需链接到https://dev59.com/DG445IYBdhLWcg3wZJiw,该链接已经涵盖了此问题)。 - Alexei Levenkov
只有你停止使用必应,我才会翻译。开玩笑,我主要使用DuckDuckGo,它主要使用必应。 :) - Quibblesome
顺便问一下,你有没有任何关于开发人员对功能的不幸期望的链接?这是一个非常有趣的主题,特别是考虑到基础框架是如何编写来促进自我发现的。 - Quibblesome

35

很多人已经回答了,但是到目前为止还没有人提到这一点......

属性与反射密切相关。反射本身就相当慢。

值得注意的是,将自定义属性标记为sealed类可以提高其运行时性能。

此外,考虑在何处使用这样的属性,并通过对您的属性进行属性(!)来指示此信息AttributeUsage。可用属性用途的列表可能会让你惊讶:

  • 程序集
  • 模块
  • 结构体
  • 枚举
  • 构造函数
  • 方法
  • 属性
  • 字段
  • 事件
  • 接口
  • 参数
  • 委托
  • 返回值
  • 泛型参数
  • 所有

AttributeUsage属性也是AttriuteUsage签名的一部分,这很酷!哇,循环依赖!

[AttributeUsageAttribute(AttributeTargets.Class, Inherited = true)]
public sealed class AttributeUsageAttribute : Attribute

14

属性是用于标记类的元数据。例如,在WinForms中通常用于从工具栏隐藏控件,但可以在您自己的应用程序中实现,以使不同类的实例以特定方式运行。

首先创建一个属性:

[AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=true)]
public class SortOrderAttribute : Attribute
{
    public int SortOrder { get; set; }

    public SortOrderAttribute(int sortOrder)
    {
        this.SortOrder = sortOrder;
    }
}

所有属性类都必须以“Attribute”作为后缀才能有效。
完成后,创建一个使用该属性的类。

[SortOrder(23)]
public class MyClass
{
    public MyClass()
    {
    }
}

现在您可以通过以下方式检查特定类的SortOrderAttribute(如果有):

public class MyInvestigatorClass
{
    public void InvestigateTheAttribute()
    {
        // Get the type object for the class that is using
        // the attribute.
        Type type = typeof(MyClass);

        // Get all custom attributes for the type.
        object[] attributes = type.GetCustomAttributes(
            typeof(SortOrderAttribute), true);

        // Now let's make sure that we got at least one attribute.
        if (attributes != null && attributes.Length > 0)
        {
            // Get the first attribute in the list of custom attributes
            // that is of the type "SortOrderAttribute". This should only
            // be one since we said "AllowMultiple=false".
            SortOrderAttribute attribute = 
                attributes[0] as SortOrderAttribute;

            // Now we can get the sort order for the class "MyClass".
            int sortOrder = attribute.SortOrder;
        }
    }
}

如果您想了解更多信息,可以查看MSDN,该网站提供了相当不错的描述。
希望这可以帮助到您!


5

在我目前正在从事的项目中,有一组各种类型的UI对象和一个编辑器,用于组装这些对象以创建页面,供主应用程序使用,有点像DevStudio中的表单设计器。这些对象存在于它们自己的程序集中,每个对象都是从UserControl派生的类,并带有自定义属性。该属性定义如下:

[AttributeUsage (AttributeTargets::Class)]
public ref class ControlDescriptionAttribute : Attribute
{
public:
  ControlDescriptionAttribute (String ^name, String ^description) :
    _name (name),
    _description (description)
  {
  }

  property String ^Name
  {
    String ^get () { return _name; }
  }

  property String ^Description
  {
    String ^get () { return _description; }
  }

private:
  String
    ^ _name,
    ^ _description;
};

我将其应用于这样一个类:

[ControlDescription ("Pie Chart", "Displays a pie chart")]
public ref class PieControl sealed : UserControl
{
  // stuff
};

这就是之前的帖子所说的。

要使用该属性,编辑器必须具有包含控件类型的 Generic::List <Type>。有一个列表框,用户可以从中拖放到页面上创建控件实例。为了填充列表框,我获取控件的 ControlDescriptionAttribute 并在列表中填写一个条目:

// done for each control type
array <Object ^>
  // get all the custom attributes
  ^attributes = controltype->GetCustomAttributes (true);

Type
  // this is the one we're interested in
  ^attributetype = ECMMainPageDisplay::ControlDescriptionAttribute::typeid;

// iterate over the custom attributes
for each (Object ^attribute in attributes)
{
  if (attributetype->IsInstanceOfType (attribute))
  {
    ECMMainPageDisplay::ControlDescriptionAttribute
      ^description = safe_cast <ECMMainPageDisplay::ControlDescriptionAttribute ^> (attribute);

    // get the name and description and create an entry in the list
    ListViewItem
      ^item = gcnew ListViewItem (description->Name);

    item->Tag = controltype->Name;
    item->SubItems->Add (description->Description);

    mcontrols->Items->Add (item);
    break;
  }
}

注意:上面的内容是C++/CLI,但将其转换为C#并不困难(我知道,C++/CLI是一种可憎的语言,但这就是我必须使用的 :-()。
您可以在大多数事物上放置属性,并且有整个预定义属性范围。上面提到的编辑器还会查找属性上的自定义属性,以描述属性及如何编辑它。
一旦你掌握了整个概念,你就会想知道你以前是怎么生活的。

5

2
“functionality”在这里不是正确的词语;它们是元数据,而不是功能。 - Marc Gravell

5

属性就像应用于类、方法或程序集的元数据。

它们非常适合各种各样的功能(调试器可视化、将事物标记为过时、将事物标记为可序列化等等)。

创建您自己的自定义属性易如反掌。请从这里开始:

http://msdn.microsoft.com/zh-cn/library/sw480ze8(VS.71).aspx


4
如前所述,属性相对容易创建。工作的另一部分是创建使用它的代码。在大多数情况下,您将在运行时使用反射来根据属性或其属性的存在来改变行为。还有一些场景,您将检查已编译代码上的属性以执行某种静态分析。例如,参数可能被标记为非空,并且分析工具可以将其用作提示。
使用这些属性并知道适当的使用场景是主要的工作内容。

3

属性基本上是您想要附加到您的类型(类、方法、事件、枚举等)的数据位。

其想法是在运行时,某些其他类型/框架/工具将查询您的类型以获取属性中的信息并对其进行操作。

例如,Visual Studio可以查询第三方控件上的属性,以确定控件的哪些属性应在设计时间的属性窗格中显示。

属性还可以用于面向方面的编程,在运行时根据修饰它们的属性注入/操作对象,并向对象添加验证、日志记录等,而不影响对象的业务逻辑。


2
要创建一个属性,打开一个 C# 源文件,键入 attribute 并按 [TAB] 键。它会展开为一个新属性的模板。

6
它是如何回答这个问题的?它应该是一条评论,而不是一个答案。 - gdoron

2
您可以使用自定义属性作为一种简单的方法,在子类中定义标记值,而无需为每个子类重复编写相同的代码。我在John Waters的博客中发现了一个不错的简明例子,介绍如何在您自己的代码中定义和使用自定义属性。
这里有一个教程:http://msdn.microsoft.com/en-us/library/aa288454(VS.71).aspx

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