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>
4 Answers 4
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.
-
\$\begingroup\$ Except it's
Foo+Bar+Baz
. \$\endgroup\$ErikE– ErikE2015年11月20日 17:32:42 +00:00Commented Nov 20, 2015 at 17:32
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.
-
\$\begingroup\$ That returns a
Type
. How does that help you get the name? \$\endgroup\$svick– svick2014年04月21日 18:09:03 +00:00Commented Apr 21, 2014 at 18:09 -
\$\begingroup\$ This returns
Bar
becauseBaz
is contained inBar
. It doesn't hold the entire sequence. \$\endgroup\$Jeroen Vannevel– Jeroen Vannevel2014年04月21日 18:22:00 +00:00Commented Apr 21, 2014 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– Malachi2014年04月21日 18:22:53 +00:00Commented Apr 21, 2014 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– Malachi2014年04月21日 20:23:44 +00:00Commented Apr 21, 2014 at 20:23
-
\$\begingroup\$ @Malachi The function doesn't return
Type
, it returnsT
. 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– svick2014年04月21日 20:28:10 +00:00Commented Apr 21, 2014 at 20:28
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)?
-
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 thatT
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– p.s.w.g2014年04月21日 19:45:14 +00:00Commented Apr 21, 2014 at 19:45
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
-
\$\begingroup\$ Is there an advantage to examining
type.MemberType
rather than Dan's approach of checkingtype.DeclaringType == null
? \$\endgroup\$Elliott Beach– Elliott Beach2018年12月10日 17:13:43 +00:00Commented Dec 10, 2018 at 17:13