我希望能够清楚地确定我所拥有的类型是自定义类类型(MyClass)还是框架(System.String)提供的类型。
在反射中是否有办法将我的类类型与系统字符串或其他由框架提供的类型区分开来?
我希望能够清楚地确定我所拥有的类型是自定义类类型(MyClass)还是框架(System.String)提供的类型。
在反射中是否有办法将我的类类型与系统字符串或其他由框架提供的类型区分开来?
安全地检查一个类型是否是一个程序集的一部分的唯一方法是检查该程序集的完全限定名称,其中包含其名称、版本、文化和公钥(如果已签名)。所有的 .Net 基类库(BCL)都使用微软的私钥进行签名。这使得其他任何人几乎不可能创建一个与基类库具有相同完全限定名称的程序集。
//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));
object.GetType().Assembly
如果有帮助的话,这是我的解决方案:
private static readonly string _RuntimeDirectory =
System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
public static bool IsSystem(this Type type)
=> type.Assembly.Location.StartsWith(_RuntimeDirectory);
并非所有的框架类都在 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标准。http://en.wikipedia.org/wiki/Base_Class_Library - LukeH
System.[something]
命名空间... 尽管我不是很喜欢这样做,但我知道一些开源项目这样做了--例如System.Data.SQLite
、System.Drawing.Html
。 - Dan TaoPublicKeyToken
的检查,以确保它是真正的系统类型。当然,如果你只想检查它是否为核心系统类型,那么你可以只做if (typeToTest.Assembly == typeof(int).Assembly)
,但这会排除更广泛的System.*
命名空间中的成员(例如,XmlDocument
)。我想这真的取决于OP究竟想要实现什么。 - LukeH