209

.NET 中的属性是什么,它们有什么用处,以及如何创建自己的属性?

4

11 回答 11

146

元数据。有关您的对象/方法/属性的数据。

例如,我可能会声明一个名为:DisplayOrder 的属性,这样我就可以轻松地控制属性在 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 设计器广泛使用它们,因此它知道如何处理自定义对象。像这样使用 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; }
    }
}

请记住,当您使用该属性时,您可以省略后缀“属性”,编译器会为您添加它。

注意:属性本身不会做任何事情 - 需要有一些其他代码使用它们。有时该代码是为您编写的,但有时您必须自己编写。例如,C# 编译器关心某些框架和某些框架使用某些框架(例如,NUnit 在加载程序集时在类上查找 [TestFixture] 并在测试方法上查找 [Test])。
因此,在创建自己的自定义属性时,请注意它根本不会影响代码的行为。您需要编写检查属性(通过反射)并对其采取行动的另一部分。

于 2008-08-21T16:18:26.767 回答
36

很多人都回答了,但到目前为止还没有人提到这个……

属性在反射中被大量使用。反射已经很慢了。

将您的自定义属性标记为类以提高其运行时性能是非常值得的。sealed

考虑在哪里使用这样的属性是合适的也是一个好主意,并将您的属性 (!) 归因于通过AttributeUsage. 可用属性用法列表可能会让您感到惊讶:

  • 集会
  • 模块
  • 班级
  • 结构
  • 枚举
  • 构造函数
  • 方法
  • 财产
  • 场地
  • 事件
  • 界面
  • 范围
  • 代表
  • 返回值
  • 通用参数
  • 全部

AttributeUsage 属性是 AttributeUsage 属性签名的一部分也很酷。哇哦循环依赖!

[AttributeUsageAttribute(AttributeTargets.Class, Inherited = true)]
public sealed class AttributeUsageAttribute : Attribute
于 2009-02-16T18:02:10.597 回答
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;
    }
}

所有属性类必须具有后缀“属性”才有效。
完成后,创建一个使用该属性的类。

[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,它的描述非常好。
我希望这对你有所帮助!

于 2008-08-21T16:33:08.587 回答
5

属性是一个包含一些功能的类,您可以将这些功能应用于代码中的对象。要创建一个,请创建一个继承自 System.Attribute 的类。

至于它们有什么好处……它们的用途几乎是无限的。

http://www.codeproject.com/KB/cs/dotnetattributes.aspx

于 2008-08-21T16:00:51.473 回答
5

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

它们适用于任何数量的事物(调试器可视化,将事物标记为过时,将事物标记为可序列化,列表无穷无尽)。

创建您自己的自定义的很容易。从这里开始:

http://msdn.microsoft.com/en-us/library/sw480ze8(VS.71).aspx

于 2008-08-21T16:02:46.617 回答
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 是可憎的,但这是我必须使用的 :-()

您可以将属性放在大多数事物上,并且有一系列预定义的属性。上面提到的编辑器还会在描述属性以及如何编辑它的属性上查找自定义属性。

一旦你明白了整个想法,你会想知道没有它们你是如何生活的。

于 2008-08-21T16:54:56.210 回答
4

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

使用属性并了解使用它们的适当场景是大部分工作。

于 2008-08-21T16:43:42.930 回答
3

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

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

因此,例如,Visual Studio 可以查询第 3 方控件的属性,以确定控件的哪些属性应在设计时出现在“属性”窗格中。

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

于 2008-08-21T17:57:10.990 回答
2

您可以使用自定义属性作为在子类中定义标记值的简单方法,而无需为每个子类一遍又一遍地编写相同的代码。我遇到了John Waters的一个很好的简洁示例,说明如何在您自己的代码中定义和使用自定义属性。

在http://msdn.microsoft.com/en-us/library/aa288454(VS.71).aspx有一个教程

于 2008-08-21T16:02:48.533 回答
2

要开始创建属性,请打开 C# 源文件,键入attribute并点击 [TAB]。它将扩展为新属性的模板。

于 2008-12-27T01:55:40.863 回答
1

属性也常用于面向方面的编程。有关此示例,请查看PostSharp项目。

于 2009-10-21T20:21:38.820 回答