c#特性

C#中的特性(Attributes)是一种强大的元数据机制,允许开发者在代码中添加声明性信息。这些信息可以在运行时通过反射(Reflection)进行访问和使用。特性可以应用于各种程序元素,如类、方法、属性、字段、参数等。

特性的定义和使用

定义自定义特性

要定义一个自定义特性,需要继承自System.Attribute类。可以通过构造函数传递参数来初始化特性。

using System;

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
public class MyCustomAttribute : Attribute
{
    public string Description { get; }

    public MyCustomAttribute(string description)
    {
        Description = description;
    }
}

在上面的示例中,MyCustomAttribute特性有一个字符串类型的Description属性。AttributeUsage特性用于指定该特性可以应用于哪些程序元素(如类和方法),是否可以继承,以及是否允许多次应用。

应用自定义特性

定义好特性后,可以将其应用于类、方法等程序元素。

[MyCustomAttribute("This is a sample class.")]
public class SampleClass
{
    [MyCustomAttribute("This is a sample method.")]
    public void SampleMethod()
    {
        Console.WriteLine("SampleMethod executed.");
    }
}
访问特性

可以使用反射来访问特性的信息。

using System;
using System.Reflection;

class Program
{
    static void Main()
    {
        // 获取类的特性
        var classAttributes = typeof(SampleClass).GetCustomAttributes(typeof(MyCustomAttribute), false);
        foreach (MyCustomAttribute attr in classAttributes)
        {
            Console.WriteLine($"Class Attribute Description: {attr.Description}");
        }

        // 获取方法的特性
        var methodAttributes = typeof(SampleClass).GetMethod("SampleMethod").GetCustomAttributes(typeof(MyCustomAttribute), false);
        foreach (MyCustomAttribute attr in methodAttributes)
        {
            Console.WriteLine($"Method Attribute Description: {attr.Description}");
        }
    }
}

常用的内置特性

C#提供了许多内置特性,以下是一些常用的内置特性及其示例:

[Obsolete]

标记某个程序元素已过时,使用时会产生编译器警告或错误。

public class Example
{
    [Obsolete("This method is obsolete. Use NewMethod instead.")]
    public void OldMethod()
    {
        Console.WriteLine("OldMethod executed.");
    }

    public void NewMethod()
    {
        Console.WriteLine("NewMethod executed.");
    }
}
[Serializable]

标记某个类可以被序列化。

[Serializable]
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}
[NonSerialized]

标记某个字段在序列化时应被忽略。

[Serializable]
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    [NonSerialized]
    private string password;
}
[DllImport]

用于调用非托管代码中的函数。

using System;
using System.Runtime.InteropServices;

public class Example
{
    [DllImport("user32.dll")]
    public static extern int MessageBox(IntPtr hWnd, string text, string caption, uint type);
}
[Conditional]

用于有条件地编译方法调用。

using System;
using System.Diagnostics;

public class Example
{
    [Conditional("DEBUG")]
    public void DebugOnlyMethod()
    {
        Console.WriteLine("This method is only called in debug mode.");
    }
}

特性的高级用法

多个特性

可以将多个特性应用于同一个程序元素。

[Serializable]
[Obsolete("This class is obsolete.")]
public class OldClass
{
    // ...
}
特性参数

特性可以接受位置参数和命名参数。

[MyCustomAttribute("This is a sample class.", AdditionalInfo = "Some additional info")]
public class SampleClass
{
    // ...
}

特性继承

特性可以继承自其他特性,这样可以复用基类特性中的逻辑和属性。

using System;

public class BaseAttribute : Attribute
{
    public string BaseInfo { get; set; }
}

public class DerivedAttribute : BaseAttribute
{
    public string DerivedInfo { get; set; }
}

[DerivedAttribute(BaseInfo = "Base information", DerivedInfo = "Derived information")]
public class SampleClass
{
    // ...
}

在上面的示例中,DerivedAttribute继承自BaseAttribute,并添加了一个新的属性DerivedInfo。在应用特性时,可以同时设置基类和派生类的属性。

特性参数

特性可以接受位置参数和命名参数。位置参数在构造函数中定义,而命名参数则是通过属性或字段来定义的。

using System;

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class MyCustomAttribute : Attribute
{
    public string Description { get; }
    public string AdditionalInfo { get; set; }

    public MyCustomAttribute(string description)
    {
        Description = description;
    }
}

[MyCustomAttribute("This is a sample class.", AdditionalInfo = "Some additional info")]
public class SampleClass
{
    // ...
}

在上面的示例中,Description是一个位置参数,通过构造函数传递,而AdditionalInfo是一个命名参数,通过属性设置。

特性的高级用法

多个特性

可以将多个特性应用于同一个程序元素。多个特性可以通过逗号分隔。

[Serializable]
[Obsolete("This class is obsolete.")]
public class OldClass
{
    // ...
}
特性目标

可以显式指定特性应用于哪个程序元素(如方法、属性、字段等)。

using System;

public class Example
{
    [method: Obsolete("This method is obsolete.")]
    public void OldMethod()
    {
        // ...
    }

    [property: Obsolete("This property is obsolete.")]
    public string OldProperty { get; set; }
}
特性与反射

通过反射可以动态地获取和操作特性的信息,这在框架和库的开发中非常有用。

using System;
using System.Reflection;

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class MyCustomAttribute : Attribute
{
    public string Description { get; }

    public MyCustomAttribute(string description)
    {
        Description = description;
    }
}

[MyCustomAttribute("This is a sample class.")]
public class SampleClass
{
    [MyCustomAttribute("This is a sample method.")]
    public void SampleMethod()
    {
        Console.WriteLine("SampleMethod executed.");
    }
}

class Program
{
    static void Main()
    {
        // 获取类的特性
        var classAttributes = typeof(SampleClass).GetCustomAttributes(typeof(MyCustomAttribute), false);
        foreach (MyCustomAttribute attr in classAttributes)
        {
            Console.WriteLine($"Class Attribute Description: {attr.Description}");
        }

        // 获取方法的特性
        var methodAttributes = typeof(SampleClass).GetMethod("SampleMethod").GetCustomAttributes(typeof(MyCustomAttribute), false);
        foreach (MyCustomAttribute attr in methodAttributes)
        {
            Console.WriteLine($"Method Attribute Description: {attr.Description}");
        }
    }
}

在上面的示例中,通过反射获取了类和方法的自定义特性,并输出了特性的描述信息。

常见的内置特性

除了前面提到的内置特性,C#还提供了许多其他有用的内置特性:

  • [Flags]:用于将枚举类型视为位域(即每个值可以是多个标志的组合)。
  • [DefaultValue]:指定属性或参数的默认值。
  • [CallerMemberName][CallerFilePath][CallerLineNumber]:用于获取调用方法的成员名称、文件路径和行号,常用于日志记录和调试。

当然,以下是关于C#特性(Attributes)的进一步详细说明,包括如何使用[CallerMemberName][CallerFilePath][CallerLineNumber]特性来获取调用方法的成员名称、文件路径和行号。

使用 Caller 信息特性

[CallerMemberName][CallerFilePath][CallerLineNumber]特性非常有用,特别是在日志记录和调试中。它们允许你在方法参数中自动捕获调用者的信息,而无需显式传递这些信息。

示例
using System;
using System.Runtime.CompilerServices;

public class Logger
{
    public void Log(string message, 
                    [CallerMemberName] string memberName = "", 
                    [CallerFilePath] string filePath = "", 
                    [CallerLineNumber] int lineNumber = 0)
    {
        Console.WriteLine($"Message: {message}");
        Console.WriteLine($"Member Name: {memberName}");
        Console.WriteLine($"File Path: {filePath}");
        Console.WriteLine($"Line Number: {lineNumber}");
    }
}

public class Example
{
    private static Logger logger = new Logger();

    public void DoSomething()
    {
        logger.Log("Doing something...");
    }

    public void DoSomethingElse()
    {
        logger.Log("Doing something else...");
    }
}

class Program
{
    static void Main()
    {
        Example example = new Example();
        example.DoSomething();
        example.DoSomethingElse();
    }
}

在上面的示例中,Logger类的Log方法使用了[CallerMemberName][CallerFilePath][CallerLineNumber]特性。这些特性会自动捕获调用Log方法的成员名称、文件路径和行号。运行程序时,输出将显示调用Log方法的具体信息。

Flags 特性

[Flags]特性用于将枚举类型视为位域,这样每个值可以是多个标志的组合。它通常用于表示一组可以组合的选项。

示例
using System;

[Flags]
public enum FileAccess
{
    Read = 1,
    Write = 2,
    Execute = 4
}

class Program
{
    static void Main()
    {
        FileAccess access = FileAccess.Read | FileAccess.Write;
        Console.WriteLine(access);  // 输出: Read, Write

        // 检查是否包含特定标志
        bool canRead = (access & FileAccess.Read) == FileAccess.Read;
        bool canWrite = (access & FileAccess.Write) == FileAccess.Write;
        bool canExecute = (access & FileAccess.Execute) == FileAccess.Execute;

        Console.WriteLine($"Can Read: {canRead}");      // 输出: Can Read: True
        Console.WriteLine($"Can Write: {canWrite}");    // 输出: Can Write: True
        Console.WriteLine($"Can Execute: {canExecute}");// 输出: Can Execute: False
    }
}

在上面的示例中,FileAccess枚举使用了[Flags]特性,使其可以表示多个组合的选项。通过按位操作,可以检查和组合这些选项。

DefaultValue 特性

[DefaultValue]特性用于指定属性或参数的默认值。它在设计时和序列化时非常有用。

示例
using System;
using System.ComponentModel;

public class Settings
{
    [DefaultValue("DefaultUser")]
    public string Username { get; set; }

    [DefaultValue(30)]
    public int Timeout { get; set; }

    public Settings()
    {
        // 应用默认值
        foreach (var property in GetType().GetProperties())
        {
            var defaultValueAttribute = (DefaultValueAttribute)Attribute.GetCustomAttribute(property, typeof(DefaultValueAttribute));
            if (defaultValueAttribute != null)
            {
                property.SetValue(this, defaultValueAttribute.Value);
            }
        }
    }
}

class Program
{
    static void Main()
    {
        Settings settings = new Settings();
        Console.WriteLine($"Username: {settings.Username}");  // 输出: Username: DefaultUser
        Console.WriteLine($"Timeout: {settings.Timeout}");    // 输出: Timeout: 30
    }
}

在上面的示例中,Settings类的UsernameTimeout属性使用了[DefaultValue]特性。在构造函数中,通过反射应用这些默认值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你一身傲骨怎能输

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值