This article analyzes the difference between Property and Attribute in C#. Share it for your reference. The specific analysis is as follows:
There are two properties in C#, namely Property and Attribute. The two have Chinese meanings, both properties and attributes, but their usage is different. For the sake of difference, this article temporarily calls Property a feature and Attribute a property.
Attribute is the protagonist of this article, and I think it is appropriate to call it attribute. Attribute means a description attached to a certain thing and is used to illustrate the various characteristics of the thing. And Attribute does this. It allows you to associate information with the C# type you define as an annotation of the type. This information is arbitrary, that is, it is not determined by the language itself, and you can create and associate any information of any type at will. You can use attributes to define design-time information and run-time information, and even run-time behavior characteristics. The key is that this information can not only be retrieved by the user as a type of annotation, but it can also be recognized by the compiler and participate in the compilation of the program as an auxiliary condition during compilation.
Some of the following content and codes are from "Inside C# Sencond Edition"
Define attributes:
An attribute is actually a class derived from a base class. The class contains several methods for accessing and checking custom properties. Although you have the right to define any class as a property, by convention it makes sense to derive a class from it. Examples are as follows:
public enum RegHives { HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CURRENT_CONFIG } public class RegKeyAttribute : Attribute { public RegKeyAttribute(RegHives Hive, String ValueName) { = Hive; = ValueName; } protected RegHives hive; public RegHives Hive { get { return hive; } set { hive = value; } } protected String valueName; public String ValueName { get { return valueName; } set { valueName = value; } } }
We have added enumerations of different registries, constructors of property classes, and two properties here. You can do many things when defining attributes. Let's take a look at how to query attributes at runtime. To query the attributes attached to a type or member at runtime, reflection must be used
Query class properties:
Suppose you want to define a property that defines the remote server on which the object will be created. If this property is not available, this information must be saved in a constant or in an application's resource file. By using properties, you just need to label the remote server name of the class with the following method:
using System; namespace QueryAttribs { public enum RemoteServers { JEANVALJEAN, JAVERT, COSETTE } public class RemoteObjectAttribute : Attribute { public RemoteObjectAttribute(RemoteServers Server) { = Server; } protected RemoteServers server; public string Server { get { return ( typeof(RemoteServers), ); } } } [RemoteObject()] class MyRemotableClass { } class Test { [STAThread] static void Main(string[] args) { Type type = typeof(MyRemotableClass); foreach (Attribute attr in (true)) { RemoteObjectAttribute remoteAttr = attr as RemoteObjectAttribute; if (null != remoteAttr) { ( "Create this object on {0}.", ); } } (); } } }
The operation result is:
Creat this object on COSETTE。
Note: The attribute class name in this example has an Attribute suffix. However, when we attach this attribute to a type or member, the Attribute suffix does not include. This is an easy way for designers of C# languages to provide. When the compiler sees a property attached to a type or member, it searches for a derived class with the specified property name. If the compiler does not find a matching class, it adds an Attribute to the specified attribute name and then searches. Therefore, it is common to use it to define the attribute class name as ending with Attribute, and ignore this part of the name when used. The following codes are named in this way.
Query method properties:
In the following example, we use attributes to define the method as a transactional method. As long as there is a TransactionableAttribute property, the code knows that the method with this attribute can belong to a transaction.
using System; using ; namespace MethodAttribs { public class TransactionableAttribute : Attribute { public TransactionableAttribute() { } } class SomeClass { [Transactionable] public void Foo() {} public void Bar() {} [Transactionable] public void Goo() {} } class Test { [STAThread] static void Main(string[] args) { Type type = (""); foreach (MethodInfo method in ()) { foreach (Attribute attr in (true)) { if (attr is TransactionableAttribute) { ( "{0} is transactionable.", ); } } } (); } } }
The operation results are as follows:
Foo is transactionable.
Goo is transactionable.
Query field properties:
Suppose there is a class that contains some fields and we want their values to be saved into the registry. To do this, you can define a property using a constructor with the enum value and string as parameters, which represents the correct registry hive and the string represents the registry value name. The registry key of a field can be queried at runtime.
using System; using ; namespace FieldAttribs { public enum RegHives { HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CURRENT_CONFIG } public class RegKeyAttribute : Attribute { public RegKeyAttribute(RegHives Hive, String ValueName) { = Hive; = ValueName; } protected RegHives hive; public RegHives Hive { get { return hive; } set { hive = value; } } protected String valueName; public String ValueName { get { return valueName; } set { valueName = value; } } } class SomeClass { [RegKey(RegHives.HKEY_CURRENT_USER, "Foo")] public int Foo; public int Bar; } class Test { [STAThread] static void Main(string[] args) { Type type = (""); foreach (FieldInfo field in ()) { foreach (Attribute attr in (true)) { RegKeyAttribute rka = attr as RegKeyAttribute; if (null != rka) { ( "{0} will be saved in" + " {1}\\\\{2}", , , ); } } } (); } } }
The running result is:
Foo will be saved in HKEY_CURRENT_USER\\Foo
As you can see, using attributes to label classes, methods, and fields can not only attach the user's custom information to the entity, but also dynamically query it at runtime. Below I will talk about some default predefined properties in C#, see the following table:
Predefined properties Valid target Description
AttributeUsage Class Specifies how to use another attribute class effectively
CLSCompliant All Indicates whether the program element is compatible with CLS
Conditional Method states that if no associated string is defined, the compiler can ignore any calls to this method.
DllImport Method Specifies the DLL location of the implementation containing the external method
STAThread Method(Main) indicates that the default threading model of the program is STA
MTAThread Method(Main) indicates that the default model of the program is multithreaded (MTA)
Obsolete Except Assembly, Module, Parameter, and Return Mark an element as unavailable, notifying the user that this element will be from future products
ParamArray Parameter allows a single parameter to be treated implicitly as params (array) parameters
Serializable Class, Struct, enum, delegate Specifies that all public and private fields of this type can be serialized
NonSerialized Field is applied to fields marked as serializable classes, indicating that these fields will not be serialized
StructLayout Class, struct Specifies the nature of the data layout of a class or structure, such as Auto, Explicit, or sequential
ThreadStatic Field (static) implements thread local storage (TLS). A given static field cannot be shared across multiple threads, each thread has a copy of this static field.
The following are some commonly used properties
1. [STAThread] and [MTAThread] properties
class Class1 { [STAThread] Static void Main( string[] args ) { } }
Use the STAThread property to specify the program's default threading model as a single-thread model. Note that the threading model only affects applications that use COM interop, and applying this property to programs that do not use COM interop will not have any effect.
2. AttributeUsage attribute
In addition to labeling custom attributes of regular C# types, you can also use the AttributeUsage attribute to define how you use these attributes. The AttributeUsage attribute calls of file records are as follows:
[AttributeUsage( validon , AllowMutiple = allowmutiple , Inherited = inherited )] ValidonThe parameters areAttributeTargetsType of,The enum value is defined as follows: public enum AttributeTargets { Assembly = 0x0001, Module = 0x0002, Class = 0x0004, Struct = 0x0008, Enum = 0x0010, Constructor = 0x0020, Method = 0x0040, Property = 0x0080, Field = 0x0100, Event = 0x200, Interface = 0x400, Parameter = 0x800, Delegate = 0x1000, All = Assembly | Module | Class | Struct | Enum | Constructor| Method | Property| Filed| Event| Interface | Parameter | Deleagte , ClassMembers = | Class | Struct | Enum | Constructor | Method | Property | Field | Event | Delegate | Interface }
AllowMultiple determines how many times a certain property can be used on a single field, and by default, all properties are used in a single time. Examples are as follows:
[AttributeUsage( , AllowMultiple = true )] public class SomethingAttribute : Attribute { public SomethingAttribute( string str ) { } } //If AllowMultiple = false, an error will be reported here[Something(“abc”)] [Something(“def”)] class Myclass { }
The Inherited parameter is an inherited flag, which indicates whether the attribute can be inherited. The default is false.
Inherited AllowMultiple Results
true false The derived attribute overrides the base attribute
true false The derived attribute and base attribute coexist
Code example:
using System; using ; namespace AttribInheritance { [AttributeUsage( , AllowMultiple=true, // AllowMultiple=false, Inherited=true )] public class SomethingAttribute : Attribute { private string name; public string Name { get { return name; } set { name = value; } } public SomethingAttribute(string str) { = str; } } [Something("abc")] class MyClass { } [Something("def")] class Another : MyClass { } class Test { [STAThread] static void Main(string[] args) { Type type = (""); foreach (Attribute attr in (true)) // (false)) { SomethingAttribute sa = attr as SomethingAttribute; if (null != sa) { ( "Custom Attribute: {0}", ); } } } } }
When AllowMultiple is set to false, the result is:
Custom Attribute : def
When AllowMultiple is set to true, the result is:
Custom Attribute : def
Custom Attribute : abc
Note that if you pass false to GetCustomAttributes, it won't search for the inheritance tree, so you can only get the derived class attributes.
property
You can attach this property to a method, so that when the compiler encounters a call to this method, if the corresponding string value is not defined, the compiler ignores the call. For example, whether the following method is compiled depends on whether the string "DEGUG" is defined:
[Condition(“DEBUG”) ] public void SomeDebugFunc() { (“SomeDebugFunc”); } using System; using ; namespace CondAttrib { class Thing { private string name; public Thing(string name) { = name; #if DEBUG SomeDebugFunc(); #else SomeFunc(); #endif } public void SomeFunc() { ("SomeFunc"); } [Conditional("DEBUG")] [Conditional("ANDREW")] public void SomeDebugFunc() { ("SomeDebugFunc"); } } public class Class1 { [STAThread] static void Main(string[] args) { Thing t = new Thing("T1"); } } }
4. Obsolete attributes
As the code continues to develop, you may have some methods that you don't need. It is possible to delete them all, but sometimes it is more appropriate to label them appropriately than to delete them, for example:
using System; namespace ObsAttrib { class SomeClass { [Obsolete("Don't use OldFunc, use NewFunc instead", true)] public void OldFunc( ) { ("Oops"); } public void NewFunc( ) { ("Cool"); } } class Class1 { [STAThread] static void Main(string[] args) { SomeClass sc = new SomeClass(); (); // (); // compiler error } } }
We set the second parameter of the Obsolete property to true, and the compiler will generate an error when the function is called.
E:\InsideC#\Code\Chap06\ObsAttrib\ObsAttrib\(20): '()' Outdated: 'Don't use OldFunc, use NewFunc instead'
5. DllImport and StructLayout properties
DllImport allows C# code to call functions in native code, and C# code calls them through the runtime function platform invoke.
If you want the runtime environment to correctly marshal the structure from managed code (or vice versa), then you need to attach attributes to the declaration of the structure. In order for structural parameters to be grouped correctly, they must be declared using the StructLayout property, indicating that the data should be laid out strictly as listed in the declaration. If you don't do this, the data will not be correctly marshalled and the application may go wrong.
using System; using ; // for DllImport namespace nativeDLL { public class Test { // [DllImport ("")] // all the defaults are OK [DllImport("user32", EntryPoint="MessageBoxA", SetLastError=true, CharSet=, ExactSpelling=true, CallingConvention=)] public static extern int MessageBoxA ( int h, string m, string c, int type); [StructLayout()] public class SystemTime { public ushort wYear; public ushort wMonth; public ushort wDayOfWeek; public ushort wDay; public ushort wHour; public ushort wMinute; public ushort wSecond; public ushort wMilliseconds; } [DllImport ("")] public static extern void GetLocalTime(SystemTime st); [STAThread] public static void Main(string[] args) { MessageBoxA(0, "Hello World", "nativeDLL", 0); SystemTime st = new SystemTime(); GetLocalTime(st); string s = ("date: {0}-{1}-{2}", , , ); string t = ("time: {0}:{1}:{2}", , , ); string u = s + ", " + t; MessageBoxA(0, u, "Now", 0); } } }
6. Accessories Properties
When using .NET to generate any type of C# project, a source code file and application source code file will be automatically generated. Contains information about the code in the accessories. Some of this information is purely information, while others allow the runtime environment to ensure unique naming and version numbers for customer code reusing your accessories.
7. Context properties
.NET cabinet shelves also provide another attribute: context attribute. Context properties provide an interception mechanism that can be processed before and after class instantiation and method calls. This feature is used for object remote calls, and it is used from COM+ Component Services and Microsoft Transaction Services (MTS) for COM-based systems.
I hope this article will be helpful to everyone's C# programming.