Reputation: 2819
In the following code fragment, Type.GetType
on the final line sometimes returns null.
string assemblyName = "My.AssemblyName, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
string typeName = "My.Namespaced.ClassName";
Type t1 = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(x => x.GetTypes())
.FirstOrDefault(x => x.FullName == typeName &&
x.Assembly.FullName == assemblyName);
string qualifiedName = t1.AssemblyQualifiedName; // this result looks sane
Type t2 = Type.GetType(qualifiedName); // returns null
As you can see, I'm using the fully qualified name returned by Type.AssemblyQualifiedName
, and as expected, the value of qualifiedName
is My.Namespaced.ClassName, My.AssemblyName, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
.
At some point in my callstack, Type.GetType
returns the expected type, but then, if we make a call into another assembly, the exact same snippet of code results in t1
finding the type, but t2
being null.
There are no generics in play here anywhere, which I understand from @Eris can sometimes result in null values for Type.AssemblyQualifiedName. In this case, AssemblyQualifiedName is valid.
I see StackOverflow is littered with duplicate questions when people have not used the assembly qualified name, but that's not the case here.
Update
I think this question can be boiled down to the following question: In what circumstances, if we have a non-null Type t
whose AssemblyQualifiedName
property is a correct combination of typename and assembly, can Type.GetType(t.AssemblyQualifiedName);
return null?
Upvotes: 4
Views: 1649
Reputation: 884
To start with, I've tested your roundtrip and found that it does work correctly.
What's the difference between your and my code? I did not want to enter full assembly name manually, instead, I obtained the right name using the assembly I reflected, in my case,
var assemblyName = Assembly.GetEntryAssembly().FullName;
Apparently, it gave me the correct name. As your and my code difference is only in the first line, I should suggest that your manually entered full assembly name is wrong. I mean, it can be a correct name, otherwise you would not obtain t1
, but not the name you need. Why, indeed, your assemblyName
string value starts with "My.AssemblyName"
, but the namespace of your typeName
is "My.Namespaced"
?! In my test, both strings match.
Conclusion: to fix your problem, you need to check two strings in the two first lines of your code. If you enter the correct names, everything else will work, I'll guarantee that.
I want to add something else. Using reflection based on constant or immediate constant string value is flawed by design. It never can be made reliable and maintainable. Names can change during the support cycle, people can misspell them, people can rename namespaces and forget those strings, and so on. Moreover, I don't think that .NET standards guarantee strongly fixed naming schemas. After all, it badly violates the S.P.O.T. principle (Signle Point of Truth).
However, I can understand that you're doing it for research or learning purposes. In working code, you need to use reliable methods never using string data on input. To do so, you have to traverse all methods, all properties, or all fields; in other cases, all members. Just for the idea: sometimes, you mark your members with a custom attribute and search by the presence of that attribute. In other cases, you may need to search a class implementing some interface, obtain its instance, and then use the interface directly, not reflecting separate methods. In these approaches, you never need the string representation of these attributes or interfaces. There can be different schemas for such reflection, what I mentioned above were just two examples to give you some basic ideas.
Upvotes: 0