【发布时间】:2011-03-11 15:15:05
【问题描述】:
我想明确确定我拥有的类型是自定义类类型 (MyClass) 还是框架提供的类型 (System.String)。
有什么方法可以通过反射将我的类类型与 system.string 或其他框架提供的类型区分开来?
【问题讨论】:
标签: c# reflection types
我想明确确定我拥有的类型是自定义类类型 (MyClass) 还是框架提供的类型 (System.String)。
有什么方法可以通过反射将我的类类型与 system.string 或其他框架提供的类型区分开来?
【问题讨论】:
标签: c# reflection types
安全检查一个类型是否是程序集的一部分的唯一方法是检查程序集的完全限定名称,其中包含其名称、版本、文化和公钥(如果已签名)。所有 .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 类来检查公钥令牌。这取决于您的需求。
【讨论】:
如果您只是想区分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));
【讨论】:
System.[something]... 我不喜欢这样做,但我知道一些开源项目可以-- 例如,System.Data.SQLite、System.Drawing.Html。
PublicKeyToken 的检查,以确保它是真正的系统类型。当然,如果您只想检查它是否是 core 系统类型,那么您可以只执行 if (typeToTest.Assembly == typeof(int).Assembly),但这会排除更广泛的 System.* 命名空间的成员(例如,XmlDocument )。我想这真的取决于 OP 到底想要达到什么目标。
您可以检查声明类型的程序集。
object.GetType().Assembly
【讨论】:
【讨论】:
并非所有框架类都以 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
【讨论】:
System.* 命名空间。 Microsoft.* 命名空间是 BCL 的超集——有时称为 FCL——并且不是 ECMA CLI 标准的一部分。 en.wikipedia.org/wiki/Base_Class_Library
我的解决方案,如果有帮助的话:
private static readonly string _RuntimeDirectory =
System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
public static bool IsSystem(this Type type)
=> type.Assembly.Location.StartsWith(_RuntimeDirectory);
【讨论】: