How do I determine if System.Type is a custom type

2020-02-10 19:03发布

问题:

I want to distinctly determine if the type that I have is of custom class type (MyClass) or one provided by the Framework (System.String).

Is there any way in reflection that I can distinguish my class type from system.string or other Framework provided types?

回答1:

The only way to safely check if a type is part of an assembly is to check the assembly's fully qualified name which contains its name, version, culture and public key (if signed). All .Net base class libraries (BCL) are signed by microsoft using their private keys. This makes it almost impossible for anyone else to create an assembly with same fully qualified name as a base class library.

//add more .Net BCL names as necessary
var systemNames = new HashSet<string>
{
"mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
};

var isSystemType = systemNames.Contains(objToTest.GetType().Assembly.FullName); 

A slightly less brittle solution is to use the AssemblyName class and skip the version number/culture check. This of course assumes the public key doesn't change between versions.

//add more .Net BCL names as necessary
var systemNames = new List<AssemblyName>
{
new AssemblyName ("mscorlib, Version=4.0.0.0, Culture=neutral, " +
                  "PublicKeyToken=b77a5c561934e089"),
new AssemblyName ("System.Core, Version=4.0.0.0, Culture=neutral, "+
                  "PublicKeyToken=b77a5c561934e089")
};

var obj = GetObjectToTest();

var objAN = new AssemblyName(obj.GetType().Assembly.FullName);

bool isSystemType = systemNames.Any(
        n =>  n.Name == objAN.Name 
           && n.GetPublicKeyToken().SequenceEqual(objAN.GetPublicKeyToken()));

Most of the BCL have been signed with the same key but not all. You could use the AssemblyName class to just check the public key token. It depends on your needs.



回答2:

If you simply want to distinguish between MyClass and string then you can check for those types directly:

Type typeToTest = GetTypeFromSomewhere();

if (typeToTest == typeof(MyClass))
    MyClassAction();
else if (typeToTest == typeof(string))
    StringAction();
else
    NotMyClassOrString();

If you need a more general check for whether or not a given type is a framework type then you could check whether it belongs to the System namespace:

// create an array of the various public key tokens used by system assemblies
byte[][] systemTokens =
    {
        typeof(System.Object)
            .Assembly.GetName().GetPublicKeyToken(),  // B7 7A 5C 56 19 34 E0 89
        typeof(System.Web.HttpRequest)
            .Assembly.GetName().GetPublicKeyToken(),  // B0 3F 5F 7F 11 D5 0A 3A 
        typeof(System.Workflow.Runtime.WorkflowStatus)
            .Assembly.GetName().GetPublicKeyToken()   // 31 BF 38 56 AD 36 4E 35 
    };

Type typeToTest = GetTypeFromSomewhere();

string ns = typeToTest.Namespace;
byte[] token = typeToTest.Assembly.GetName().GetPublicKeyToken();

bool isSystemType = ((ns == "System") || ns.StartsWith("System."))
                    && systemTokens.Any(t => t.SequenceEqual(token));


回答3:

You can check the Assembly in which the type is declared.

object.GetType().Assembly


回答4:

Check if the assembly belongs to the CLR library:

myType.Module.ScopeName == "CommonLanguageRuntimeLibrary"

as explained here.



回答5:

Not all framework classes start in the System namespace (they can also be Microsoft etc).

As such, you could probably compare the location of a known framework class with that of the type that you are testing such as:

  if (String.CompareOrdinal(
        Path.GetDirectoryName(typeof(String).Assembly.Location), 
        Path.GetDirectoryName(typeof(MyType).Assembly.Location)
      ) == 0)
  {
    //Framework Type
  }
  else
  {
    //3rd Party DLL
  }

Not the greatest solution; but safer than just testing if the namespace starts with System (I could create a namespace that starts with System that isn't a framework class).

Edit

Also, in addition to the above test, it wouldn't hurt to verify that the type is loaded from the Global Assembly Cache:

typeof(MyType).Assembly.GlobalAssemblyCache