Microsoft.Net框架程序设计学习笔记(22):接口与完全限定接口名技巧
时间:2011-03-29 来源:辛勤的代码工
接口仅仅是一个包含着一组虚方法的抽象类型。
接口中也可以定义事件、无参属性、含参属性(即索引器),因为它们都不过是映射到方法上的语法缩写而已。
接口类型的名称要加一个大写的字母I前缀。接口定义允许使用修饰符--如public、protected、private、internal。
一个值类型可以实现一个或多个接口,但当我们将一个值类型实例转型为一个接口类型时,该值类型实例必须被执行装箱。因为接口总被认为是引用类型,且它们定义的方法总是虚方法。未装箱的值类型没有指向类型方法表的指针。
当我们创建可扩展的应用程序时,接口应该处于中心位置。假设我们正在编写一个应用程序,且希望其他人创建的类型能被我们的应用程序无缝地加载使用。下面就提供了设计这样的应用程序的方法。
- 创建一个程序集,然后在其中定义接口,接口的方法将用于应用程序和插件组件的通信机制。在为接口方法定义参数和返回值时,我们应该尽可能地使用定义在MSCorLib.dll中的其他接口和类型。如果确实希望传递或返回我们自己定义的数据类型时,则应该把它们也定义在该程序集中。一旦建立好接口定义后,我们应该给该程序集指定一个强命名,然后将其打包并部署到合作伙伴和用户那里。这样以后就把该程序集视作一个恒定不变的程序集。
- 创建一个单独的程序集用于包含我们的应用程序所使用的其他类型。该程序集引用到前一个程序集中定义的接口和类型。我们可以任意改变该程序集中的代码,而不会影响到接口,这样对插件开发人员不会造成任何影响。
- 插件开发人员会在他们的程序集中定义自己的类型,也将会引用到我们前面定义的接口程序集中的类型。插件开发人员也可以随时提供新版程序集,而不影响到我们的程序集。
使用接口改变已装箱值类型中的字段
我们可以利用接口来欺骗C#使其改变已装箱类型中的字段。
代码示例:
//定义Change方法接口
interface IChangeBoxedPoint
{
void Change(int x, int y);
}
struct Point : IChangeBoxedPoint
{
public int x, y;
public void Change(int x, int y)
{
this.x = x;
this.y = y;
}
public override string ToString()
{
return String.Format("({0}, {1})", x, y);
}
}
static void Main()
{
Point p = new Point();
p.x = p.y = 1;
Console.WriteLine(p); //显示(1, 1)
p.Change(2, 2);
Console.WriteLine(p); //显示(2, 2)
Object o = p;
Console.WriteLine(o); //显示(2, 2)
((Point) o).Change(3, 3); //在堆栈上改变拆箱后临时的Point对象
Console.WriteLine(o); //显示(2, 2)
//对p执行装箱,然后改变已装箱对象,最后丢弃
((IChangeBoxedPoint) p).Change(4, 4);
Console.WriteLine(p); //显示(2, 2)
//改变已装箱对象,并显示其内容
((IChangeBoxedPoint) o).Change(5, 5);
Console.WriteLine(o); //显示(5, 5)
}
这是一段比较有趣的代码,帮助我们加深对值类型装箱、拆箱的理解。
完全限定接口名技巧
当一个类型需要实现多个接口,而碰巧它们的方法又有相同的名称和签名时,如何处理?
示例代码:
public interface IWindow
{
object GetMenu();
}
public interface IRestaurant
{
object GetMenu();
}
public class Pizza : IWindow, IRestaurant
{ //完全限定接口名称
object IWindow.GetMenu(){......}
object IRestaurant.GetMenu(){......}
//这个GetMenu方法与接口没有任何关系
public object GetMenu() {......}
}
C#编译器在辨析接口成员实现时,会按照“先完全限定接口成员,后非完全限定接口成员”的顺序来进行辨析。
注意,上面的完全限定接口方法没有被声明为public,不能这样做的原因是这些方法有着双重身份:它们有时为公有方法,有时又为私有方法。看下面的代码:
static void SomeMethod()
{
Pizza p = new Pizza();
object menu;
//调用公有的GetMenu方法,使用Pizza引用
//完全限定接口方法将为私有方法,不能被调用
menu = p.GetMenu();
//调用IWindow的GetMenu方法。使用IWindow引用
menu = ((IWindow) p).GetMenu();
//调用IRestaurant的GetMenu方法,使用IRestaurant引用
menu = ((IRestaurant) p).GetMenu();
}
当我们在一个类型中用完全限定接口名来定义一个接口方法时,该方法被认为是私有方法,不能使用类型本身的引用来调用它。但当我们将该类型的引用转型为一个接口时,该接口中定义的方法可被调用,这时它又成为一个公有方法。
完全限定接口名这一技术被广泛用于FCL类库中。比如List<T>类型中实现ICollection接口的SyncRoot属性时就使用到了这一技巧。List<T>类型唯有转换为ICollection的引用才能获取SyncRoot属性。