【问题标题】:How do I determine if System.Type is a custom type or a Framework type?如何确定 System.Type 是自定义类型还是框架类型?
【发布时间】:2011-03-11 15:15:05
【问题描述】:

我想明确确定我拥有的类型是自定义类类型 (MyClass) 还是框架提供的类型 (System.String)。

有什么方法可以通过反射将我的类类型与 system.string 或其他框架提供的类型区分开来?

【问题讨论】:

    标签: c# reflection types


    【解决方案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】:

      如果您只是想区分MyClassstring,那么您可以直接检查这些类型:

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

      【讨论】:

      • 并不是真正的防弹,例如如果命名空间被称为“SystemUtil”;)
      • @thomas:我在发帖后不久就意识到了这一点,但还没来得及编辑就被叫走了。现已修复。
      • 我很确定您实际上可以编写自己的程序集并创建命名空间 System.[something]... 我不喜欢这样做,但我知道一些开源项目可以-- 例如,System.Data.SQLiteSystem.Drawing.Html
      • @Dan:确实如此,尽管这也是一种非常糟糕的做法。我进行了编辑以包括对程序集的PublicKeyToken 的检查,以确保它是真正的系统类型。当然,如果您只想检查它是否是 core 系统类型,那么您可以只执行 if (typeToTest.Assembly == typeof(int).Assembly),但这会排除更广泛的 System.* 命名空间的成员(例如,XmlDocument )。我想这真的取决于 OP 到底想要达到什么目标。
      【解决方案3】:

      您可以检查声明类型的程序集。

      object.GetType().Assembly
      

      【讨论】:

      • 那么问题来了:如何知道程序集是 .NET 框架的一部分?
      【解决方案4】:

      检查程序集是否属于CLR库:

      myType.Module.ScopeName == "CommonLanguageRuntimeLibrary"
      

      正如here 解释的那样。

      【讨论】:

        【解决方案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 开头的命名空间,而不是框架类)。

        编辑

        另外,除了上述测试之外,验证类型是否从全局程序集缓存中加载也没有什么坏处:

        typeof(MyType).Assembly.GlobalAssemblyCache
        

        【讨论】:

        • 据我所知,BCL 仅包含 System.* 命名空间。 Microsoft.* 命名空间是 BCL 的超集——有时称为 FCL——并且不是 ECMA CLI 标准的一部分。 en.wikipedia.org/wiki/Base_Class_Library
        • @LukeH:真的很好。我要强调的重点是不要依赖以 System.开头的命名空间。正如其他解决方案所表明的那样,明确总是最好的选择。这是一张“穷人”的支票,我以为我会扔掉以防万一。
        【解决方案6】:

        我的解决方案,如果有帮助的话:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-09-12
          • 2017-10-13
          • 2014-11-22
          • 2013-01-29
          • 1970-01-01
          • 1970-01-01
          • 2021-06-01
          • 2014-04-12
          相关资源
          最近更新 更多