Implementing a plug-in schema in a C # program

Source: Internet
Author: User
Tags foreach definition bool ftp implement new features tostring valid
Program | architecture

Original Author:
Shawn Patrick Walcheske

Translator:
The summer mast of the

Introduction

In the. NET Framework of the C # language, and others. NET language provides a lot of powerful features and mechanisms. Some of them are brand new, while others are copied from the previous language platform. However, this ingenious combination has created some interesting ways to solve our problems. This article will describe how to take advantage of these wonderful features, using Plug-ins ( Plug-ins) mechanism to build scalable solutions. A brief example will also be provided later, and you can even use this to replace the stand-alone programs that have been used extensively in many systems. In a system, There may be many programs that often require data processing. There may be one program that handles the employee's information, and the other is used to manage the customer relationship. In most cases, the system is always designed as a number of separate programs, with little interaction between them, Often used to copy code to share. In fact, such a situation can be designed as plug-ins, and then a single program to manage these plug-ins. This design allows us to better share common methods in different solutions and provide a sense of unity.

Picture one is a screenshot of an example program. The user interface is no different from other common programs. The entire form is split vertically into two blocks. The left pane is a tree menu that displays the list of Plug-ins, under each plug-in's branch, Lists the data that the plug-in manages. The right pane is used to edit the data for the selected plug-in on the left. Each plug-in provides an interface for editing the data. The picture shows an ingenious work area.

Start

Then, the main program must be able to load plug-ins, and then communicate with these plug-ins in order to achieve our design. All of these implementations can have many different approaches, depending on the language and platform the developer chooses. If you are selecting C # and. NET, then reflection (reflection) The mechanism can be used to load plug-ins, and its interfaces and abstract classes can be used to communicate with Plug-ins.

To better understand the communication between the main program and the plug-in, you can first understand the design pattern. The design pattern was first presented by Erich Gamma [1], which uses architecture and object ideas to implement a common communication model. Regardless of whether the component has different inputs and outputs, As long as they have similar structures. Design patterns can help developers use the proven object-oriented theory to solve problems. In fact, it is the language that describes the solution, not the specifics of the problem or the specifics of the programming language. The key point of design pattern strategy is how to decompose the whole solution according to function. This decomposition is done by separating the different functions of the main program. This allows communication between the main program and the subroutine to be done through a well-designed interface. We can get these two benefits right away: First, software projects are divided into smaller, unrelated units, and workflow design can be easier, Smaller pieces of code mean that the code is easier to establish and maintain. The second advantage is that changing the behavior of the program does not concern the operation of the main program, the main program does not care about the subroutine, as long as there is a common communication mechanism between them is enough.

[Establish interface]

In a C # program, an interface is used to define the functionality of a class. The interface defines the expected method, property, and event information. To use an interface, each specific function must complete the described function strictly according to the definition of the interface. The list shows the interface of the above example program: Iplug. This interface defines four methods: Getdata,geteditcontrol,save and print. These four definitions do not describe exactly how they are done, but they ensure that the class supports the Iplug interface, which is guaranteed to support the invocation of these methods.

[Custom Properties]

Before you look at the code, the discussion always has to be transferred to the attribute customization. Attribute customization is one of the great new features of. NET, which is a common structure for all programming languages. For example, a function is used to identify the public,private that can access permissions, Or the Protect flag is an attribute of this function. Attribute customization is so exciting because programmers will no longer be able to choose from the limited set of attributes that the language itself provides. A custom attribute is actually a class, it inherits from System.Attribute, Its code is allowed to be self-describing. Attribute customization can be applied to most structures, including classes in C #, methods, events, fields, and attributes. The sample code fragment defines two custom properties: Plugdisplaynameattribute and Plugdescriptionattribute, The classes in all Plug-ins must support both properties. List Two is the class that is used to define the Plugdisplaynameattribute. This property is used to display the contents of the plug-in node. When the program is running, the main program can use reflection (reflection) to get the property value.

[Plugin (Plug-ins)]

The example program above includes the execution of two plug-ins. These plug-ins are defined in EmployeePlug.cs and CustomerPlug.cs. List three shows a partial definition of the Employeeplug class. Here are some key points.

1. This class implements the Iplug interface. Because the main program simply does not know how the classes within the plug-in are defined, it is important that the main program use the Iplug interface to communicate with each plug-in. This design utilizes the "polymorphism" in the object-oriented concept. Polymorphism allows runtime, by reference to the base class To invoke the method in the implementing derived class.
2. This class is identified by two attributes so that the main program can determine if the plugin is valid. In C #, to identify a property for a class, you have to declare the attribute before the definition of the class and enclose the contents in parentheses.
3. For simplicity, the example uses only data that is written directly to the code. And if the plugin is a formal product, then the data should always be in a database or file, and all the data should be managed by the plugin itself. The data for the Employeeplug class is stored here with the EmployeeData object, which is also a type and implements the Iplugdata interface. The Iplugdata interface is defined in IPlugData.cs, which provides the most basic data exchange capabilities, Used for communication between the main program and the plug-in. All objects that support the Iplugdata interface will provide a notification when the underlying data changes. This notice is actually the occurrence of the DataChanged event.
4. When the main program needs to display a list of data contained in a plugin, It invokes the GetData method. This method returns an array of Iplugdata objects. This allows the main program to use the ToString method for each object in the array to get the data to establish each node of the tree. The ToString method is an overload of the EmployeeData class. Used to display the employee's name.
The 5.IPlug interface also defines the save and print methods. The purpose of defining these two methods is to notify a plugin when there is a need to print or save data. The Employeeplug class is used to implement the ability to print and save data. When using the Save method, The location where the data needs to be saved will be provided at the time of the method call. This assumes that the main program will query the user for information such as the path. The query for path information is the service that the main program provides to each plug-in. For the Print method, The main program passes the options and content to an instance of the System.Drawing.Printing.PrintDocument class. In both cases, the interaction with the user is consistent and is provided by the main program.

[Reflection (Reflection)]

After a plug-in is defined, the next step is to see how the main program loads the plug-in. To achieve this goal, the main program uses a reflection mechanism. Reflection is used in. NET to view type information at run time. With the help of reflection mechanism, The type information will be loaded and viewed. This can be done by examining the type to determine whether the plug-in is valid. If the type passes the check, then the plugin can be added to the main program interface and can be manipulated by the user.

The sample program uses the. NET Framework's three built-in classes to use reflection: System.reflection.assembly,system.type, and System.activator.

The System.Reflection.Assembly class describes the. NET assembly. In. NET, an assembly is a hive. For a typical Windows program, the assembly is configured as a single WIN32 executable with specific additional information to adapt. NET running environment. Assemblies can also be configured as WIN32 DLLs (dynamic link libraries), as well as with. NET requires additional information. The System.Reflection.Assembly class can obtain information about the assembly at run time. This information includes the type information that the assembly contains.

The System.Type class describes the type definition. A type declaration can be a class, interface, array, struct, or enumeration. After a class has been loaded, the System.Type class can be used to enumerate the methods, properties, events, and interfaces supported by the class.

The System.activator class is used to create an instance of a class.

[Load plugin]

Listing four shows the Loadplugs method. The Loadplugs method is defined in HostForm.cs and is a private non-static method of the Hostform class. The Loadplugs method loads the available plug-in files using the. NET reflection mechanism. and verify that they meet the requirements that are used by the main program, and then add them to the tree display area of the main program. This method includes the following steps:

1. By using the System.IO.Directory class, our code can use wildcard characters to find all files with a. plug extension. The static method of the directory class GetFiles can return an array of System.String types To get the physical path of each file that meets the requirements.
2. After you get an array of path strings, you can start loading the files into the System.Reflection.Assembly instance. The code that establishes the Asdsembly object uses the Try/catch code block so that if a file is not valid. NET assembly, an exception is thrown, and the program then pops up a MessageBox dialog box that tells the user that the file cannot be loaded. The loop continues until all the files have been traversed.
3. After an assembly has been loaded, the code traverses all accessible type information and checks to see if the Hostcommon.iplug interface is supported.
4. If all types support the Hostcommon.iplug interface, the code continues to validate these types, checking to see if the properties that have been predefined for the plug-in are supported. If there is no support, Then a hostcommon.plugnotvalidexception type of exception will be thrown, and again, the main program will pop up a MessageBox, telling the user the specific information of the error. The loop continues until all the files have been traversed.
5. Finally, if these types support the Hostcommon.iplug interface and have defined all the attributes that need to be defined, then it will be packaged as a Plugtreenode instance. This instance is added to the main program's Tree display area.

Achieve

The main program framework is designed to be two assemblies. The first assembly is Host.exe, which provides the Windows Forms interface for the main program. The second assembly is HostCommon.dll, which provides all the type definitions needed to communicate between the main program and the plug-in. For example, The Iplug interface is configured inside the HostCommon.dll so that it can be accessed by both the main program and the plug-in. The two assemblies are in a single folder, and the additional assemblies as plug-ins need to be configured together. Those assemblies are configured in the Plugs folder ( A subfolder of the main program directory). The Employeeplug class is defined in the Employee.plug assembly, The Customerplug class is defined in the Customer.plug assembly. This example specifies that the plug-in file should have a. plug extension. In fact, these plug-ins are a common. NET class library file, but usually the library file uses the. dll extension. Here's A. Plug. A special extension has no effect on the running of the program, but it gives the user a clearer idea that this is a plug-in file.

[Comparison of design]

It is not necessarily the case that the design is correct as an example program. For example, when developing a C # program with Plug-ins, you do not necessarily need to use attributes. The example uses two custom attributes, which can also be implemented with a new definition of the parameters of the two iplug interfaces. Here you choose to use attributes, It is because the name of the plugin and its description are, in essence, the property of a thing that conforms to the specification. Of course, using attributes can cause the main program to need more code on reflection. For different needs, the designer always needs to make a reasonable decision.

Summary

The sample program is designed to be as simple as possible to help understand the communication between the main program and the plug-in. When actually doing the product, you can make a lot of improvements to meet the practical requirements. For example:

1. By adding more methods, properties, events to the Iplug interface, you can increase the communication points between the main program and the plug-in. More interaction between the two allows Plug-ins to do more.
2. You can allow users to actively select plug-ins that need to be loaded.

[Source code]
The complete source code for the sample program can be downloaded here.
Ftp://ftp.cuj.com/pub/2003/2101/walchesk.zip

Notes
[1] Erich Gamma et al. Design Patterns (Addison-wesley, 1995).

Picture one:


List one: the Iplug interface

public interface Iplug
{
Iplugdata[] GetData ();
Plugdataeditcontrol GetEditControl (Iplugdata Data);
BOOL Save (string Path);
BOOL Print (PrintDocument Document);
}

List two: the Plugdisplaynameattribute class definition

[AttributeUsage (AttributeTargets.Class)]
public class PlugDisplayNameAttribute:System.Attribute
{
private string _displayname;

Public Plugdisplaynameattribute (String DisplayName): Base ()
{
_displayname=displayname;
Return
}

public override string ToString ()
{
return _displayname;
}

List three: A partial listing of the Employeeplug class definition

[Plugdisplayname ("Employees")]
[Plugdescription ("This plug are for managing employee data")]
public class EmployeePlug:System.Object, Iplug
{
Public iplugdata[] GetData ()
{
iplugdata[] data = new employeedata[]
{
New EmployeeData ("Jerry", "Seinfeld")
, New EmployeeData ("Bill", "Cosby")
, New EmployeeData ("Martin", "Lawrence")
};

return data;
}

Public Plugdataeditcontrol GetEditControl (Iplugdata Data)
{
return new Employeecontrol ((EmployeeData) Data);
}

public bool Save (string Path)
{
Implementation not shown
}

public bool Print (PrintDocument Document)
{
Implementation not shown
}
}

List four: The method loadplugs

private void Loadplugs ()
{
string[] files = Directory.GetFiles ("Plugs", "*.plug");

foreach (string f in Files)
{

Try
{
Assembly a = Assembly.LoadFrom (f);
system.type[] types = A.gettypes ();
foreach (System.Type Type in Types)
{
if (type. GetInterface ("Iplug")!=null)
{
if (type. GetCustomAttributes (typeof (Plugdisplaynameattribute),
False). Length!=1)
throw new Plugnotvalidexception (type,
"Plugdisplaynameattribute is not supported");
if (type. GetCustomAttributes (typeof (Plugdescriptionattribute),
False). Length!=1)
throw new Plugnotvalidexception (type,
"Plugdescriptionattribute is not supported");

_tree. Nodes.Add (New Plugtreenode (type));
}
}
}
catch (Exception e)
{
MessageBox.Show (E.message);
}
}

Return
}

[About author]
Shawn Patrick Walcheske is a software development engineer in Phoenix, Arizona State, USA. He is also a Microsoft Certified Solution developer and Sun Certified Programmer for the Java 2 Platform. You can contact him here, questions@walcheske.com.

[Translator note]
I've thought about it before. NET inside how to implement plug-in mechanism, do to do to always feel that the design is not good enough. And yesterday on the Internet accidentally found this article, the writing is really great, so after reading, decided to translate it, before and after a total of about 10 hours. The translation may not be very good, please forgive me. What's wrong with this? Please do not hesitate to correct me.
My e-mail:sunmast@vip.163.com.



Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.