Tải bản đầy đủ - 0 (trang)
3-12. Instantiate an Object Using Reflection

3-12. Instantiate an Object Using Reflection

Tải bản đầy đủ - 0trang

CHAPTER 3 ■ APPLICATION DOMAINS, REFLECTION, AND METADATA



Solution

Obtain a Type object representing the type of object you want to instantiate, call its GetConstructor

method to obtain a System.Reflection.ConstructorInfo object representing the constructor you want to

use, and execute the ConstructorInfo.Invoke method.



How It Works

The first step in creating an object using reflection is to obtain a Type object that represents the type you

want to instantiate. (See recipe 3-10 for details.) Once you have a Type instance, call its GetConstructor

method to obtain a ConstructorInfo representing one of the type’s constructors. The most commonly

used overload of the GetConstructor method takes a Type array argument and returns a ConstructorInfo

representing the constructor that takes the number, order, and type of arguments specified in the Type

array. To obtain a ConstructorInfo representing a parameterless (default) constructor, pass an empty

Type array (use the static field Type.EmptyTypes or new Type[0]); don’t use null, or else GetConstructor

will throw a System.ArgumentNullException. If GetConstructor cannot find a constructor with a signature

that matches the specified arguments, it will return null.

Once you have the desired ConstructorInfo, call its Invoke method. You must provide an object

array containing the arguments you want to pass to the constructor. Invoke instantiates the new object

and returns an object reference to it, which you must cast to the appropriate type.

Reflection functionality is commonly used to implement factories in which you use reflection to

instantiate concrete classes that either extend a common base class or implement a common interface.

Often, both an interface and a common base class are used. The abstract base class implements the

interface and any common functionality, and then each concrete implementation extends the base

class.

No mechanism exists to formally declare that each concrete class must implement constructors

with specific signatures. If you intend third parties to implement concrete classes, your documentation

must specify the constructor signature called by your factory. A common approach to avoiding this

problem is to use a default (empty) constructor and configure the object after instantiation using

properties and methods.



The Code

The following code fragment demonstrates how to instantiate a System.Text.StringBuilder object using

reflection and how to specify the initial content for the StringBuilder (a string) and its capacity (an

int):

using System;

using System.Text;

using System.Reflection;

namespace Apress.VisualCSharpRecipes.Chapter03

{

class Recipe03_12

{

public static StringBuilder CreateStringBuilder()

{



130



www.it-ebooks.info



CHAPTER 3 ■ APPLICATION DOMAINS, REFLECTION, AND METADATA



// Obtain the Type for the StringBuilder class.

Type type = typeof(StringBuilder);

// Create a Type[] containing Type instances for each

// of the constructor arguments - a string and an int.

Type[] argTypes = new Type[] { typeof(System.String),

typeof(System.Int32) };

// Obtain the ConstructorInfo object.

ConstructorInfo cInfo = type.GetConstructor(argTypes);

// Create an object[] containing the constructor arguments.

object[] argVals = new object[] { "Some string", 30 };

// Create the object and cast it to StringBuilder.

StringBuilder sb = (StringBuilder)cInfo.Invoke(argVals);

return sb;

}

}

}

The following code demonstrates a factory to instantiate objects that implement the IPlugin

interface (first used in recipe 3-7):

using System;

using System.Reflection;

namespace Apress.VisualCSharpRecipes.Chapter03

{

// A common interface that all plug-ins must implement.

public interface IPlugin

{

string Description { get; set; }

void Start();

void Stop();

}

// An abstract base class from which all plug-ins must derive.

public abstract class AbstractPlugin : IPlugin

{

// Hold a description for the plug-in instance.

private string description = "";

// Sealed property to get the plug-in description.

public string Description

{

get { return description; }

set { description = value; }

}



131



www.it-ebooks.info



CHAPTER 3 ■ APPLICATION DOMAINS, REFLECTION, AND METADATA



// Declare the members of the IPlugin interface as abstract.

public abstract void Start();

public abstract void Stop();

}

// A simple IPlugin implementation to demonstrate the PluginFactory class.

public class SimplePlugin : AbstractPlugin

{

// Implement Start method.

public override void Start()

{

Console.WriteLine(Description + ": Starting...");

}

// Implement Stop method.

public override void Stop()

{

Console.WriteLine(Description + ": Stopping...");

}

}

// A factory to instantiate instances of IPlugin.

public sealed class PluginFactory

{

public static IPlugin CreatePlugin(string assembly,

string pluginName, string description)

{

// Obtain the Type for the specified plug-in.

Type type = Type.GetType(pluginName + ", " + assembly);

// Obtain the ConstructorInfo object.

ConstructorInfo cInfo = type.GetConstructor(Type.EmptyTypes);

// Create the object and cast it to StringBuilder.

IPlugin plugin = cInfo.Invoke(null) as IPlugin;

// Configure the new IPlugin.

plugin.Description = description;

return plugin;

}

public static void Main(string[] args)

{

// Instantiate a new IPlugin using the PluginFactory.

IPlugin plugin = PluginFactory.CreatePlugin(

"Recipe03-12", // Private assembly name

"Apress.VisualCSharpRecipes.Chapter03.SimplePlugin",

// Plug-in class name.

"A Simple Plugin"

// Plug-in instance description

);



132



www.it-ebooks.info



CHAPTER 3 ■ APPLICATION DOMAINS, REFLECTION, AND METADATA



// Start and stop the new plug-in.

plugin.Start();

plugin.Stop();

// Wait to continue.

Console.WriteLine("\nMain method complete. Press Enter.");

Console.ReadLine();

}

}

}



■ Tip The System.Activator class provides two static methods named CreateInstance and

CreateInstanceFrom that instantiate objects based on Type objects or strings containing type names. The key

difference between using GetConstructor and Activator is that the constructor used by Activator is implied by

the constructor arguments you pass to CreateInstance or CreateInstanceFrom. Using GetConstructor, you

can determine exactly which constructor you want to use to instantiate the object. See the description of the

Activator class in the .NET Framework SDK documentation for more details.



3-13. Create a Custom Attribute

Problem

You need to create a custom attribute.



Solution

Create a class that derives from the abstract base class System.Attribute. Implement constructors,

fields, and properties to allow users to configure the attribute. Use System.AttributeUsageAttribute to

define the following:





Which program elements are valid targets of the attribute







Whether you can apply more than one instance of the attribute to a program

element







Whether the attribute is inherited by derived types



How It Works

Attributes provide a mechanism for associating declarative information (metadata) with program

elements. This metadata is contained in the compiled assembly, allowing programs to retrieve it through

reflection at runtime without creating an instance of the type. (See recipe 3-14 for more details.) Other



133



www.it-ebooks.info



CHAPTER 3 ■ APPLICATION DOMAINS, REFLECTION, AND METADATA



programs, particularly the CLR, use this information to determine how to interact with and manage

program elements.

To create a custom attribute, derive a class from the abstract base class System.Attribute. Custom

attribute classes by convention should have a name ending in Attribute (but this is not essential). A

custom attribute must have at least one public constructor—the automatically generated default

constructor is sufficient. The constructor parameters become the attribute’s mandatory (or positional)

parameters. When you use the attribute, you must provide values for these parameters in the order they

appear in the constructor. As with any other class, you can declare more than one constructor, giving

users of the attribute the option of using different sets of positional parameters when applying the

attribute. Any public nonconstant writable fields and properties declared by an attribute are

automatically exposed as named parameters. Named parameters are optional and are specified in the

format of name/value pairs where the name is the property or field name. The following example will

clarify how to specify positional and named parameters.

To control how and where a user can apply your attribute, apply the attribute

AttributeUsageAttribute to your custom attribute. AttributeUsageAttribute supports the one

positional and two named parameters described in Table 3-3. The default values specify the value that is

applied to your custom attribute if you do not apply AttributeUsageAttribute or do not specify a value

for that particular parameter.

Table 3-3. Members of the AttributeUsage Type



Parameter



Type



Description



Default



ValidOn



Positional



A member of the System.AttributeTargets

enumeration that identifies the program

elements on which the attribute is valid



AttributeTargets.All



AllowMultiple



Named



Whether the attribute can be specified more than

once for a single element



False



Inherited



Named



Whether the attribute is inherited by derived

classes or overridden members



True



The Code

The following example shows a custom attribute named AuthorAttribute, which you can use to identify

the name and company of the person who created an assembly or a class. AuthorAttribute declares a

single public constructor that takes a string containing the author’s name. This means users of

AuthorAttribute must always provide a positional string parameter containing the author’s name. The

Company property is public, making it an optional named parameter, but the Name property is read-only—

no set accessor is declared—meaning that it isn’t exposed as a named parameter.

using System;

namespace Apress.VisualCSharpRecipes.Chapter03

{

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Assembly,

AllowMultiple = true, Inherited = false)]



134



www.it-ebooks.info



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

3-12. Instantiate an Object Using Reflection

Tải bản đầy đủ ngay(0 tr)

×