Edit Found the original contract name resolution by .NET MEF, the code below is not useful anymore. See response!
My issue was to use existing code, to make a MEF ExportFactory, imported via [Import] attribute to a property, return instances of Moq mocks (for unit testing). Problem was, that the original code always used the Type.FullName property value as contract name. This worked fine for non-generic types, but not for generics.
Generic FullName is like:
My.Assembly.GenericType`1[My.Assembly.InnerType]
but MEF expects a name like:
My.Assembly.GenericType(My.Assembly.InnerType)
The complete unit test code actually extends the MEF ExportProvider class, to register ExportDefinition instances for an ExportFactory and it's ProductDefinition. The contract name goes to a metadata dictionary, using the key CompositionConstants.ExportTypeIdentityMetadataName.
This following code is only for the contract name conversion. It calls itself recursively, for generic arguments which are generic themselves. The regular expression matches by the `[digits] name extension, used by .NET for generic types. It would be nice to see a generally better way, or already existing functions from MEF, if such exists.
Edit: I added the incrementor and code for open (unspecified) generics, which have names with indexed curly brackets: instead of IMyType<string,int> IMyType<,>, resolved to contract name IMyType({0},{1}). Regex has also been changed to match, if no type names come after `[digits].
private static readonly Regex GenericRegex =
new Regex(@"^(?<fullNameNonGeneric>\S+?)`\d+(\[.*\])*$",
RegexOptions.Compiled);
public static string ResolveGenericMefContractName(this Type type)
{
return ResolveGenericMefContractName(type, new IndexIncrementor());
}
private static string ResolveGenericMefContractName(Type type, IndexIncrementor incrementor)
{
var fullName = type?.FullName;
if (fullName == null)
{
return null;
}
var match = GenericRegex.Match(fullName);
if (!match.Success)
{
return type.FullName;
}
var fullNameNonGeneric = match.Groups["fullNameNonGeneric"].Value;
var genericArgs = type.GetGenericArguments();
return fullNameNonGeneric + "(" +
string.Join(",", genericArgs.Select(
ga => ResolveGenericMefContractName(ga, incrementor)
?? "{" + incrementor.GetAndIncrementValue() + "}"))
+ ")";
}
private class IndexIncrementor
{
private int _value;
public int GetAndIncrementValue() => _value++;
}