7
\$\begingroup\$

Suppose I have a nested class structure like this:

namespace My.Namespace {
    public class Foo {
        public class Bar {
            public class Baz {
                public string Test { get; set; }
            }
        }
    }
}

I need to get the nested type name of Baz without the namespace, i.e. Foo+Bar+Baz in a generic method. Unfortunately, .Name will just return Baz and .FullName will give me the namespace as well. Right now I'm using this:

protected T LoadSample<T>(string fileName) 
{
    var t = typeof(T);
    var nestedTypeName = t.Namespace == null
        ? t.FullName                                 // T is declared outside of a namespace
        : t.FullName.Substring(t.Namespace.Length + 1);
    ...
}

But this just looks ugly. Is there a more standard method for getting just Foo+Bar+Baz in this situation?

To clarify what's going on inside this method, this is part of a unit test for an XML serialization class. LoadSample will load an XML file, wrap it in a little more XML to create an 'envelope', attempt to parse it, and return the result if it succeeds. The problem is that the 'envelope' that the XML serialization class expects the XML to specify the type name in a particular format. For example:

<baz>
    <test>Testing</test>
</baz>

Will be wrapped in an evelope like this:

<evelope>
    <messageType>urn:My.Namespace:Foo+Bar+Baz</messageType>
    <message>
        <test>Testing</test>
    </message>
</evelope>
\$\endgroup\$
5
\$\begingroup\$

Well, one brute force way of doing it is to recurse over the type's DeclaringType property:

public static string TypeName (Type type)
{
   if(type.DeclaringType == null)
      return type.Name;

   return TypeName(type.DeclaringType) + "." + type.Name;
}

Running the following program:

static void Main(string[] args)
{
   var type = typeof(My.Namespace.Foo.Bar.Baz);
   var name = TypeName(type);
}

returns the name Foo.Bar.Baz as you would expect.

\$\endgroup\$
  • \$\begingroup\$ Except it's Foo+Bar+Baz. \$\endgroup\$ – ErikE Nov 20 '15 at 17:32
2
\$\begingroup\$

It sounds like this might be what you are looking for I think.

Type.ReflectedType Property (.NET 4.5)

GetType(t).ReflectedType;

I am not sure what version of the framework that you are using though.


this may only work if you nest it

(GetType(GetType(t).ReflectedType).ReflectedType).ToString() + "." + (GetType(t).ReflectedType).ToString() + "." + (GetType(t).Name).ToString();

that is kind of messy too though.


There is also the GetNestedTypes and GetNestedType Methods which will return the types along the way.

you may have to create a function that will recursively build the nested class name for you.

\$\endgroup\$
  • \$\begingroup\$ That returns a Type. How does that help you get the name? \$\endgroup\$ – svick Apr 21 '14 at 18:09
  • \$\begingroup\$ This returns Bar because Baz is contained in Bar. It doesn't hold the entire sequence. \$\endgroup\$ – Jeroen Vannevel Apr 21 '14 at 18:22
  • \$\begingroup\$ @JeroenVannevel see Edits. it's still really messy, the OP might be a better fit, unless they go with creating a function. \$\endgroup\$ – Malachi Apr 21 '14 at 18:22
  • \$\begingroup\$ @svick, this function takes a string parameter, but returns a type, so the OP doesn't really need the name, they need the type, probably for comparison based on the comment on Mat'sMug's answer. \$\endgroup\$ – Malachi Apr 21 '14 at 20:23
  • \$\begingroup\$ @Malachi The function doesn't return Type, it returns T. It's hard to say what exactly it does in the ... part, but I don't think wild guessing about it makes much sense. \$\endgroup\$ – svick Apr 21 '14 at 20:28
1
\$\begingroup\$

System.Type inherits a Name property from System.Reflection.MemberInfo (MSDN), which returns the name of the member, not the fully qualified name. As you've noticed that returns Baz when you want Foo.Bar.Baz.

Either you use reflection to get the three types and concatenate their .Name, or you do what you did and chop off the namespace from the fully qualified name.

And it's ugly. What are we trying to accomplish here?

protected T LoadSample<T>(string fileName)

If you're loading data from a file named by the fileName argument, and using reflection to instantiate Baz, I'd suggest abstracting this dirt away into a SampleFactory<T>... I'm curious about what happens to the type's name in the rest of your code; reading your code makes me wonder why you would need to do this to load a sample T from fileName.

Is it possible that your life would be simpler if T had a new() type constraint (i.e. if T had a parameterless constructor)?

\$\endgroup\$
  • 2
    \$\begingroup\$ It's actually a unit test for a serialization class. The sample file contains XML that gets loaded, wrapped in some more XML to create an 'envelope' which specifies the type name, and then that entire XML is passed on to the serializer which is supposed to parse it and generate the desired type. The serializer expects the envolope to specify the message type as "urn:message:My.Namespace:Foo+Bar+Baz". The goal is to verify that T can be deserialized with this tool, without having to encode the entire envelope in my sample file, because I will be using the same samples for other tests. \$\endgroup\$ – p.s.w.g Apr 21 '14 at 19:45
1
\$\begingroup\$

When reflecting on types you could look at the MemberType to determine whether to add the + sign before the name of the type:

public static string GetTypeName(Type type)
{
    if (type.MemberType == MemberTypes.NestedType)
    {
        return string.Concat(GetTypeName(type.DeclaringType), "+", type.Name);
    }

    return type.Name;
}

With your example types the output of:

GetTypeName(typeof(My.Namespace.Foo.Bar.Baz));

is Foo+Bar+Baz

\$\endgroup\$
  • \$\begingroup\$ Is there an advantage to examining type.MemberType rather than Dan's approach of checking type.DeclaringType == null ? \$\endgroup\$ – Warlike Chimpanzee Dec 10 '18 at 17:13

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.