Как определить, является ли System.Type настраиваемым типом или типом Framework?

Я хочу четко определить, имеет ли тип, который у меня есть, тип пользовательского класса (MyClass) или один, предоставленный Framework (System.String).

Есть ли какой-либо способ отражения, что я могу отличить свой тип класса от system.string или других типов, предоставляемых Framework?

Ответ 1

Единственный способ безопасно проверить, является ли тип частью сборки, - проверить полное имя сборки, которое содержит его имя, версию, культуру и открытый ключ (если они подписаны). Все библиотеки базового класса .Net(BCL) подписываются microsoft с использованием их секретных ключей. Это делает почти невозможным для кого-либо еще создать сборку с таким же полным именем, как библиотека базового класса.

//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); 

Немного менее хрупкое решение заключается в использовании AssemblyName и пропустить номер версии/проверку культуры. Это, конечно, предполагает, что открытый ключ не изменяется между версиями.

//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()));

Большинство BCL были подписаны с одним и тем же ключом, но не со всеми. Вы можете использовать класс AssemblyName, чтобы просто проверить токен открытого ключа. Это зависит от ваших потребностей.

Ответ 2

Если вы просто хотите различать MyClass и string, вы можете напрямую проверить эти типы:

Type typeToTest = GetTypeFromSomewhere();

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

Если вам нужна более общая проверка того, является ли данный тип типом структуры, то вы можете проверить, принадлежит ли это пространству имен System:

// 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

Вы можете проверить сборку, в которой объявлен тип.

object.GetType().Assembly

Ответ 4

Проверьте, принадлежит ли сборка библиотеке CLR:

myType.Module.ScopeName == "CommonLanguageRuntimeLibrary"

как описано здесь.

Ответ 5

Не все классы классов начинаются в пространстве имен System (они также могут быть Microsoft и т.д.).

Таким образом, вы, вероятно, можете сравнить местоположение известного класса рамки с типом тестируемого вами типа, например:

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

Не самое большое решение; но безопаснее, чем просто тестировать, если пространство имен начинается с System (я мог бы создать пространство имен, которое начинается с System, который не является классом инфраструктуры).

Edit

Кроме того, в дополнение к вышеуказанному тесту, не помешает проверить, что тип загружен из глобального сборочного кэша:

typeof(MyType).Assembly.GlobalAssemblyCache