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

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

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


person Nilotpal Das    schedule 04.07.2010    source источник


Ответы (6)


Единственный способ безопасно проверить, является ли тип частью сборки, — это проверить полное имя сборки, которое содержит ее имя, версию, культуру и открытый ключ (если он подписан). Все библиотеки базовых классов .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, чтобы просто проверить токен открытого ключа. Это зависит от ваших потребностей.

person Bear Monkey    schedule 04.07.2010

Если вы просто хотите различать 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));
person LukeH    schedule 04.07.2010
comment
Не совсем пуленепробиваемый, например, если пространство имен называется SystemUtil;) - person Thomas Kjørnes; 04.07.2010
comment
@thomas: Я понял это вскоре после публикации, но меня отозвали, прежде чем я смог отредактировать. Теперь исправлено. - person LukeH; 05.07.2010
comment
Я почти уверен, что вы действительно можете написать свою собственную сборку и создать пространство имен System.[something]... Я не большой поклонник этого, но я знаю, что некоторые проекты с открытым исходным кодом делают это, например, System.Data.SQLite, System.Drawing.Html. - person Dan Tao; 05.07.2010
comment
@Dan: Это очень верно, хотя это тоже довольно плохая практика. Я отредактировал, чтобы включить проверку сборки PublicKeyToken, чтобы убедиться, что это истинный тип системы. Конечно, если вы хотите только проверить, что это основной системный тип, вы можете просто использовать if (typeToTest.Assembly == typeof(int).Assembly), но это исключит членов более широкого пространства имен System.* (например, XmlDocument). Я думаю, это действительно зависит от того, чего именно пытается достичь ОП. - person LukeH; 05.07.2010

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

object.GetType().Assembly
person Itay Karo    schedule 04.07.2010
comment
Итак, возникает вопрос: как узнать, что сборка является частью .NET framework? - person Hans Passant; 04.07.2010

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

myType.Module.ScopeName == "CommonLanguageRuntimeLibrary"

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

person Dejan    schedule 22.01.2017

Не все классы фреймворка начинаются в пространстве имен 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, которое не является классом фреймворка).

Изменить

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

typeof(MyType).Assembly.GlobalAssemblyCache
person Chris Baxter    schedule 04.07.2010
comment
Насколько мне известно, BCL состоит только из System.* пространств имен. Пространства имен Microsoft.* являются расширенным набором BCL, иногда называемым FCL, и не являются частью стандарта ECMA CLI. en.wikipedia.org/wiki/Base_Class_Library - person LukeH; 05.07.2010
comment
@LukeH: Действительно хороший момент. Важным моментом, к которому я стремился, было не полагаться на пространство имен, начинающееся с System. И, как показали другие решения, быть явным всегда лучше; это чек бедняка, который я думал выбросить на всякий случай. - person Chris Baxter; 05.07.2010

Мое решение, если оно может помочь:

private static readonly string _RuntimeDirectory =
    System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
public static bool IsSystem(this Type type)
    => type.Assembly.Location.StartsWith(_RuntimeDirectory);
person d34th53nd3r    schedule 18.05.2021