If I have one type MyClass, register with
[Export(typeof(Myclass))]
attribute, and
[PartCreationPolicy(CreationPolicy.Shared)]
will MEF do global lookup again or it caches somewhere internally
Yes, MEF perfoms some caching and widely uses lazy initialization, if you question is about MEF performance:
1) metadata (composable parts, export definitions and import definitions) is cached. Example:
public override IEnumerable<ExportDefinition> ExportDefinitions
{
get
{
if (this._exports == null)
{
ExportDefinition[] exports = this._creationInfo.GetExports().ToArray<ExportDefinition>();
lock (this._lock)
{
if (this._exports == null)
{
this._exports = exports;
}
}
}
return this._exports;
}
}
2) exported values are cached too:
public object Value
{
get
{
if (this._exportedValue == Export._EmptyValue)
{
object exportedValueCore = this.GetExportedValueCore();
Interlocked.CompareExchange(ref this._exportedValue, exportedValueCore, Export._EmptyValue);
}
return this._exportedValue;
}
}
Of course, when using CreationPolicy.NonShared
, exported value becomes created again and again, when you requesting it. But even in this case "global lookup" isn't performed, because metadata is cached anyway.
Although the values/metadata might be partially cached, doing some performance testing shows that some lookup is performed every time a call to GetExportedValue
is made. So if you have many calls where you need to get the value, you should do the caching yourself.
namespace MEFCachingTest
{
using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using System.Diagnostics;
using System.Reflection;
public static class Program
{
public static CompositionContainer Container { get; set; }
public static ComposablePartCatalog Catalog { get; set; }
public static ExportedClass NonCachedClass
{
get
{
return Container.GetExportedValue<ExportedClass>();
}
}
private static ExportedClass cachedClass;
public static ExportedClass CachedClass
{
get
{
return cachedClass ?? (cachedClass = Container.GetExportedValue<ExportedClass>());
}
}
public static void Main()
{
Catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
Container = new CompositionContainer(Catalog);
const int Runs = 1000000;
var stopwatch = new Stopwatch();
// Non-Cached.
stopwatch.Start();
for (int i = 0; i < Runs; i++)
{
var ncc = NonCachedClass;
}
stopwatch.Stop();
Console.WriteLine("Non-Cached: Time: {0}", stopwatch.Elapsed);
// Cached.
stopwatch.Restart();
for (int i = 0; i < Runs; i++)
{
var cc = CachedClass;
}
stopwatch.Stop();
Console.WriteLine(" Cached: Time: {0}", stopwatch.Elapsed);
}
}
[Export]
[PartCreationPolicy(CreationPolicy.Shared)]
public class ExportedClass
{
}
}
For more variations, look at this gist: https://gist.github.com/DanielRose/d79f0da2ef61591176ce
On my computer, Windows 7 x64, .NET 4.5.2:
Non-Cached: Time: 00:00:02.1217811
Cached: Time: 00:00:00.0063479
Using MEF 2 from NuGet:
Non-Cached: Time: 00:00:00.2037812
Cached: Time: 00:00:00.0023358
In the actual application where I work, this made the application 6x slower.
It does a lookup every time, when you use [PartCreationPolicy(CreationPolicy.NonShared)]
. You then have to implement the caching yourself.
The default implementation is using a Singleton pattern. This equals the attribute [PartCreationPolicy(CreationPolicy.Shared)]
. This is the best practice.
For more information, read http://blogs.microsoft.co.il/blogs/bnaya/archive/2010/01/09/mef-for-beginner-part-creation-policy-part-6.aspx