症状:
请看下面三个代码以及对应的编译步骤,能看出什么问题出来吗?
ITest.cs:
1. using System;
|
2.
|
3. public interface ITest
|
4. {
|
5. void TestMethod();
|
6. }
|
TestClass.cs:
1. using System;
|
2.
|
3. public class TestClass : ITest
|
4. {
|
5. public void TestMethod()
|
6. {
|
7. Console.WriteLine("TestClass.TestMethod");
|
8. }
|
9. }
|
Program.cs
1. using System;
|
2. using System.Reflection;
|
3.
|
4. public class Program
|
5. {
|
6. public static void Main()
|
7. {
|
8. Assembly assembly = Assembly.LoadFrom("TestClass.dll");
|
9. Type type = assembly.GetType("TestClass");
|
10. object instance = Activator.CreateInstance(type);
|
11. ((ITest)instance).TestMethod();
|
12. }
|
13. }
|
编译命令:
csc /t:library /out:TestClass.dll TestClass.cs ITest.cs
|
csc Program.cs ITest.cs
|
|
代码很简单,我们定义了一个公开的接口ITest,提供了一个实现该接口的类TestClass,然后在Program.cs通过发射创建TestClass的一个实例,然后调用ITest接口里面的一些方法。这是一个实现插件系统的最基本的操作, 但是当你执行编译生成出来的Program.exe的时候,程序抛出一个InvalidCastException:
Unhandled Exception: System.InvalidCastException: Unable to cast object of type
'TestClass' to type 'ITest'.
at Program.Main()
|
类的全名(或者说强签名)实际上包含Assembly完整名称的。
10. object instance = Activator.CreateInstance(type);
|
11. ((ITest)instance).TestMethod();
|
这两行,在CLR看来其实等于(分解成几步伪码):
[TestClass,Version=0.0.0.0]ITest instance = Activator.CreateInstance(type);
[Program, Version=0.0.0.0]ITest temp = ([Program, Version=0.0.0.0]ITest)instance;
Temp.TestMethod();
所以说,两个接口不是同一个接口。
但是如果在程序里面使用到两个不同的Assembly,并且这两个Assembly都包含名称相同的类,如果你的Winform程序里面需要寄宿WPF控件,或者反过来,你就经常会碰到这种问题,例如两种程序框架里面都包含TextBox类。当然啦,你可以说WPF和Winform的类的命名空间不一样,只要在用的时候指定完整的命名空间就可以了,但是如果你碰到连命名空间都一样的情况下,或者说,如果你的程序同时引用到同一个Assembly的两个不同版本的文件,又如何解决呢?C#没有提供语法支持你在类名前面加上Assembly的名字—虽然IL里面提供了对应的语法。
幸好,C#编译器csc.exe为我们提供了一个解决方案,csc.exe提供了一个参数/reference:<alias>=<file>可以让你为你程序所引用的一个Assembly指定别名,然后在源代码里面使用这个别名将名称重复的类改名,具体做法如下:
ITest.cs:
1. using System;
|
2.
|
3. public interface ITest
|
4. {
|
5. void TestMethod();
|
6. }
|
TestClass.cs:
1. using System;
|
2.
|
3. public class TestClass : ITest
|
4. {
|
5. public void TestMethod()
|
6. {
|
7. Console.WriteLine("TestClass.TestMethod");
|
8. }
|
9. }
|
Program.cs
1. extern alias ITestDll;
|
2. using System;
|
3. using System.Reflection;
|
4.
|
5. public interface ITest
|
6. {
|
7. void TestMethod();
|
8. }
|
9.
|
10. public class Program
|
11. {
|
12. public static void Main()
|
13. {
|
14. Assembly assembly = Assembly.LoadFrom("TestClass.dll");
|
15. Type type = assembly.GetType("TestClass");
|
16. object instance = Activator.CreateInstance(type);
|
17. ((ITestDll::ITest)instance).TestMethod();
|
18. }
|
19. }
|
编译命令:
csc /t:library ITest.cs
|
csc /t:library /r:ITest.dll /out:TestClass.dll TestClass.cs
|
csc /r:ITestDll=ITest.dll Program.cs
|
|
这样就解决了类名重复的问题了。