/// <summary>
/// Fixes up a full generic classname to the syntax tha XML docs
/// use which uses a `1 to specify the number of generic parameters
/// </summary>
/// <param name="ClassName"></param>
/// <returns></returns>
public string FixUpGenericName(string ClassName)
{
int At = ClassName.IndexOf("<");
// *** IF it's a generic type we need to fix it up
if (At > -1)
{
string TGenericParms = wwUtils.ExtractString(ClassName, "<", ">");
string[] TParms = TGenericParms.Split(',');
// *** Build the classname: MyClass<T,T1> -> MyClass`2
ClassName = ClassName.Replace("<" + TGenericParms + ">","`" + TParms.Length.ToString() );
}
return ClassName;
}
That part is easy. But it took a while to get the whole thing straightened out to turn a CodeDom signature like this:
HelpTest.HelpClass<TKey, TValue>.SimpleGenericParm(TValue,TKey)
and map it to the Reflection based syntax which should look like this:
HelpTest.HelpClass`2.SimpleGenericParm(`1,`0)
As it turns out it's not that difficult to, which the following code demonstrates:
/// <summary>
/// This routine converts a full CodeDom sigature into a Reflection
/// compatible Signature used by wwReflection's assembly import
///
/// HelpTest.HelpClass<TKey, TValue>.SimpleGenericParm(TValue,TKey)
/// HelpTest.HelpClass`2.SimpleGenericParm(`1,`0)
/// </summary>
/// <param name="Signature"></param>
/// <returns></returns>
public string GetReflectionSignatureFromCodeDom(string Signature)
{
MatchCollection matches = Regex.Matches(Signature, "<.*?>");
foreach (Match match in matches)
{
string[] GenericTypes = match.Value.Replace("<", "").Replace(">", "").Split(new char[1] { ',' },
StringSplitOptions.RemoveEmptyEntries);
// *** Write the typename with the `2 prefix: Note if there are generic types (ie. List<TValue>) that
// *** use Generic types these types will already be replaced and this match won't replace
// *** which is actually the correct behavior - these types will later updated to { }'s instead of < >
Signature = Signature.Replace(match.Value, "`" + GenericTypes.Length.ToString());
// *** Now replace the generic parameters
for (int x = 0; x < GenericTypes.Length; x++)
{
Signature = Signature.Replace(GenericTypes[x].Trim(), "`" + x.ToString());
}
}
// *** If there are any generic parameters left they are for generic type refs in parms
// *** Replace with {`1}
Signature = Signature.Replace('<','{').Replace('>','}');
return Signature;
}
Now this little bit of code actually solves a number of problems in my code both for popping up Html Help Builder in context,
but also for editing and providing signatures for added members. It can now be called from another reusable routine
that generically returns a signature from a code element:
/// <summary>
/// Returns a fully qualified signature for a given method.
/// Example Signatures:
/// NameSpace.Class.Property
/// NameSpace.Class.Method(type Parm1, type Parm2)
/// </summary>
/// <param name="element"></param>
/// <returns></returns>
public string GetSignatureFromCodeElement(CodeElement element)
{
string Sig = element.FullName;
// *** TODO: This needs some work for Generic types. My<GenericParm> vs. My`1 in Type
if (element.Kind == vsCMElement.vsCMElementFunction)
{
CodeFunction func = element as CodeFunction;
string Parms = "";
foreach( CodeParameter parm in func.Parameters )
{
string TypeName = parm.Type.AsFullName;
if (TypeName == null || TypeName == "")
TypeName = parm.Type.AsString;
Parms += TypeName + ",";
}
if (Parms.Length > 0)
Parms = Parms.Substring(0,Parms.Length-1);
// *** Must fix up the constructor to match the constructor #ctor syntax
if ( ( (CodeFunction) element).FunctionKind == vsCMFunction.vsCMFunctionConstructor)
Sig = Sig.Substring(0,Sig.LastIndexOf(".")+1 ) + "#ctor";
Sig = Sig + "(" + Parms + ")";
}
Sig = this.GetReflectionSignatureFromCodeDom(Sig);
return Sig;
}
Ah - the benefits of blogging! When I started writing this post I was fishing for a hopeful solution and here I am at the
end of the post when the solution actually presented itself. Sweet!