In the previous article, we generally understood that the MEF framework was used to automate the scanning and assembly of extension components. In this article, we move on, starting with the initial definition of the public interface and learning how to use MEF in one step.
In the previous article we know that for each extension component that implements the common specification, it needs to be exported and then assembled automatically in our main application file. This raises the question: why do I need to export?
If you remember, before we use VC + + to write a. dll file, the function that needs to be supplied to someone else is marked as an export function, so that someone else can call the function we write. Like our home, we usually have a living room, since it is called the living room, of course, show to the guests. There are guests, we will be in the living room reception, of course, we do not want to let guests into our bedroom, it is a more private place.
So, for the extensions we're writing, we're going to tell MEF which classes should be scanned, just like our website, and we'll filter which pages allow search engines to crawl.
To mark a component as exportable, you need to append the System.ComponentModel.Composition.ExportAttribute attribute to the type's definition code. We can look at the definition of the Exportattribute class.
Truefalse)]publicclass Exportattribute:attribute
From the definition we see, the Exportattribute attribute can be used for classes and members of classes, and can often be appended to the entire class to represent the entire type for export.
Determine which export type conforms to the conditions of the assembly container import, based on the Contractname and Contracttype properties.
Contractname we can specify when attaching exportattribute, or we can not specify it. The Contracttype property specifies the type to export, and if not specified, the default is the type that is currently being exported. Like what:
// public Interface Public Interface imember{string getmembertype ();} [Export] Public class vipmember:imember{publicstring getmembertype () {return "VIP member ";}}
In the above example, the public interface is Imember, the class Vipmember implements the interface and is marked as an export type, but does not specify the Contractname and Contracttype properties. In this case, the default contract type is Vipmember, and the default export type is the type of the class to which the attribute is attached.
Then, we define a Genmember class.
[Export] Public class genmember:imember{public string Getmembertype () {return' General Membership " ;}}
At this point, for the Genmember class, the exported type is genmember.
As you may have found, the disadvantage of defining an export type is that there is no contract type that is passed, so that it cannot be automatically identified when the extension component is assembled, because each of our classes expands to a new contract type (CONTRACTTYPE). This causes the code of the main application to be modified repeatedly, not once and for all. So, in general, we should set the Contracttype as the type of the public interface, as in the example above Imember. Therefore, we should change the code to:
[Export (typeof(Imember))] Public classvipmember:imember{ Public stringGetmembertype () {return "VIP Members";}} [Export (typeof(Imember))] Public classgenmember:imember{ Public stringGetmembertype () {return "General Membership";}}
Such a change, to meet the requirements, as long as the implementation of the Imember interface and the addition of the Exportattribute type will be automatically scanned by the assembly container, even if you expand the 1e+29 components, it can be scanned and assembled.
You can define Contractname in Exportattribute if you want the assembly container to require a specific class when scanning the type. This makes the matching conditions of the scan type more precise and reduces the scope of the search. Of course, this also reduces the intelligence, because in the assembly code, you also want to match the contract name, which also makes the main application code will be constantly modified.
After the type is exported, it can be supplied to the assembly container for assembly. Take our example above, and then we assemble the Vipmember and Genmember classes.
classProgram{[import (typeof(Imember))] PublicList<imember>allmembers;Static voidMain (string[] args) {//the way the type is found is the current assemblyAssemblycatalog Catalog=NewAssemblycatalog (assembly.getexecutingassembly ());//Creating an assembly containerCompositioncontainer Container=Newcompositioncontainer (catalog); Program P=NewProgram ();//Start AssemblyTry{container.composeparts (P); Console.WriteLine ("----------The test call----------");foreach(Imember minchp.allmembers) {Console.WriteLine (M.getmembertype ());}}Catch(Compositionexception CeX) {Console.WriteLine (CeX. Message);} while(Console.readkey (). Key! =consolekey.escape);}}
Since we have extended two classes to imember, in order to have them all imported, a list<imember> field is defined in the program class, and we want to put all the imported types into this list. When attaching importattribute to Exportattribute, we only define contracttype, so we still use Contracttype to match it when we import it.
The app doesn't seem to be a problem, and it's estimated to work, so we can press F5 to see the results.
Oh, God, there's been a miracle. From the exception message, we learned that Importattribute cannot flag a field that imports multiple types of lists at once. How to solve it? Mo Mo Urgent, look at this attribute:
//Summary://specifies that the property, field, or parameter should be passed System.ComponentModel.Composition.Hosting.CompositionContainer//The object is populated with all matching exports. [AttributeUsage (Attributetargets.property| Attributetargets.field | Attributetargets.parameter, AllowMultiple =false, inherited =false)] Public classImportmanyattribute:attribute, Iattributedimport Yes, this Attribute is dedicated to importing multiple types, so just change the code to this:classProgram{[importmany (typeof(Imember))] PublicList<imember>allmembers;
Run again and we can get the expected results.
In today's era, we have to be green in everything, and our program is no exception. The above program seems to be no big deal. However, if the type we are importing may carry some large data, we can save some of the resource overhead if we allow them to defer initialization . Although the delayed initialization is not very pleasant to call, but it is not difficult to understand the concept, as if you buy a sports lottery in the jackpot, but the provider is not directly to the bill to you, you may be the first to give you a cheque, then, you pay by cheque to the bank to withdraw money.
This lazy initialization is similar, and it is not initialized immediately at the time of declaration, until you use it. The System.lazy<t> class will lead us to a greener modern life, and it will wait until you access its Value property to initialize. So, we also put the above code environmental protection.
classProgram{[importmany (typeof(Imember))] PublicList<lazy<imember>>allmembers;Static voidMain (string[] args) {...Try{container.composeparts (P); Console.WriteLine ("----------The test call----------");foreach(Lazy<imember> LZinchp.allmembers) {Console.WriteLine (LZ. Value.getmembertype ());}} ...
Finally, to mention an unimportant thing, the classes used in our code are
Using System.ComponentModel.Composition;
Using System.ComponentModel.Composition.Hosting;
Please refer to the System.ComponentModel.Composition (. dll) assembly.
The complete code for this example is as follows:
usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;usingSystem.ComponentModel.Composition;usingSystem.ComponentModel.Composition.Hosting;usingSystem.Reflection;namespacemefexam{//public Interface Public Interfaceimember{stringGetmembertype ();} [Export (typeof(Imember))] Public classvipmember:imember{ Public stringGetmembertype () {return "VIP Members";}} [Export (typeof(Imember))] Public classgenmember:imember{ Public stringGetmembertype () {return "General Membership";}} classProgram{[importmany (typeof(Imember))] PublicList<lazy<imember>>allmembers;Static voidMain (string[] args) {//the way the type is found is the current assemblyAssemblycatalog Catalog=NewAssemblycatalog (assembly.getexecutingassembly ());//Creating an assembly containerCompositioncontainer Container=Newcompositioncontainer (catalog); Program P=NewProgram ();//Start AssemblyTry{container.composeparts (P); Console.WriteLine ("----------The test call----------");foreach(Lazy<imember> LZinchp.allmembers) {Console.WriteLine (LZ. Value.getmembertype ());}}Catch(Compositionexception CeX) {Console.WriteLine (CeX. Message);} while(Console.readkey (). Key! =consolekey.escape); }}}
Article reproduced in: East Evil Solitary
Combat MEF (2): Export & Import