5
\$\begingroup\$

I was working on some TT code generation to get rid of mundane C# codings in VS2013 Express Edition, and after researching online, created the following helper methods to facilitate Type declarations.

I am able to use it successfully in my code generation but wonder if they can be improved further or if there's any scenario which they do not cater for.

public static String GetActualTypeName(Type type, bool bAlias = true, bool bFullQualified = false)
{
    Type nullable = Nullable.GetUnderlyingType(type);
    String name = null;

    if (nullable != null) // handles Nullable types
    {
        name = (bAlias) ? GetAliasName(nullable, bFullQualified)
            : (bFullQualified) ? nullable.FullName : nullable.Name;

        return String.Format("{0}?", name);
    }

    if (!type.GenericTypeArguments.Any()) // handles non generic types
    {
        name = (bAlias) ? GetAliasName(type, bFullQualified)
            : (bFullQualified) ? type.FullName : type.Name;

        return name;
    }

    name = (bFullQualified) ? type.FullName : type.Name;

    // recrusive looping into generic type tree for the required type names 
    String[] names = type.GenericTypeArguments
        .Select(t => GetActualTypeName(t, bAlias, bFullQualified))
        .ToArray();         

    return String.Format("{0}<{1}>", name.Substring(0, name.IndexOf('`')), String.Join(",", names));
}

public static String GetAliasName(Type type, bool bFullQualified = false)
{
    String alias = null;

    using (var provider = new Microsoft.CSharp.CSharpCodeProvider())
    {
        var typeRef = new System.CodeDom.CodeTypeReference(type);

        alias = provider.GetTypeOutput(typeRef);
    }

    int index = alias.LastIndexOf(".");

    if (bFullQualified || index == -1)
        return alias;

    return alias.Substring(index + 1);
}
\$\endgroup\$
6
\$\begingroup\$
bool bAlias

Don't use Hungarian notation on names. When you look at the parameter, you can already see that it's a bool, there is no reason to repeat that in the name.


String name = null;

Since it seems you never want to use that null value, don't initialize the variable at all.

Also, it's more common to write string instead of String.


name = (bAlias) ? GetAliasName(nullable, bFullQualified)
    : (bFullQualified) ? nullable.FullName : nullable.Name;

What's the point of the parentheses around bAlias and bFullQualified. They don't seem to serve any purpose.


String[] names = type.GenericTypeArguments
    .Select(t => GetActualTypeName(t, bAlias, bFullQualified))
    .ToArray();    

I think names is too general here. I think something like typeArgumentNames would be better here (even if it's significantly longer).

Also, String.Join() can accept IEnumerable<string>, so the ToArray() is unnecessary here.


name.Substring(0, name.IndexOf('`'))

Wouldn't this be more readable as name.Split('`')[0]? (It would also be less efficient, but that might not matter to you.)


String.Join(",", names)

I realize this is for generated code, so beauty does not matter much, but I would still prefer to add a space between the type arguments, i.e. String.Join(", ", names).


using (var provider = new Microsoft.CSharp.CSharpCodeProvider())

Neither CSharpCodeProvider not its base class, CodeDomProvider override Dispose(), it's only there because they inherit from Component.

This means that there is no need to use using here. But even if you know this, you might still choose to include the using, just to be on the safe side.

Also, since you're already using CSharpCodeProvider for this, why not use it to replace your whole GetActualTypeName()? You would lose the choice of specifying the form of the output, but it might be worth it for so much simplification.

\$\endgroup\$
  • 1
    \$\begingroup\$ "It's more common to use string instead of String"... I'd just like to add that some people like to use lower case for declaring a type and uppercase when calling the class's static methods. \$\endgroup\$ – RubberDuck Jul 7 '15 at 16:07
2
\$\begingroup\$

I'll accept one ternary condition, but they really shouldn't be nested.

if (!type.GenericTypeArguments.Any()) // handles non generic types
{
    name = (bAlias) ? GetAliasName(type, bFullQualified)
        : (bFullQualified) ? type.FullName : type.Name;

    return name;
}

If you're going to insist on it, why bother using a variable? Just return it directly. I really do recommend being a little more verbose here though. We don't get any extra points for writing hard to read on liners.

if (!type.GenericTypeArguments.Any())
{
    if (bAlias)
    {
        return GetAliasName(type, bFullQualified);
    }
    else
    {
        return (bFullQualified) ? type.FullName : type.Name;
    }

}
\$\endgroup\$
  • 1
    \$\begingroup\$ Agreed on the verbose aspect. Guess it's bit of "old habit die hard". Thanks! \$\endgroup\$ – Alex Apr 8 '15 at 6:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.