.net 2.0中委托增加了两个特性:Covariance和Contravariance,从而在匹配方法和委托签名的时候引入了一定的弹性。这两个单词似乎是来自数学中关于矩阵的概念,颇难找到合适的词汇来翻译,这里就姑且直译为“协变”和“逆变”吧。个人感觉MSDN上的定义非常绕口,这里就不直译了,还是先来用伪代码来描述新的匹配过程吧,其实代码也不算严格的伪代码了^_^:
public class MethodDelegateMatch ...{
/**//// <summary>
/// 判断某方法与某委托签名是否匹配。(.net 2.0)
/// </summary>
/// <remarks>
/// DelegateInfo类可以通过Microsoft.Cci命名空间下的类构造:)
/// </remarks>
/// <param name="methodInfo"></param>
/// <param name="delegateInfo"></param>
/// <returns>true:可匹配。false:匹配失败</returns>
public static bool IsMatchable(MethodInfo methodInfo, DelegateInfo delegateInfo) ...{
return IsReturnTypeMatchable(methodInfo, delegateInfo) &&
IsParamsMatchable(methodInfo, delegateInfo);
}
/**//// <summary>
/// 判断返回类型是否可匹配
/// </summary>
/// <remarks>
/// 在.net 2.0之前,要求返回类型相同。
///
/// 在.net 2.0中,仅要求方法的返回类型可以赋给委托签名中声明的返回类型,
/// 这样就允许方法返回签名中规定类型的子类。此谓之“协变”也。
/// </remarks>
/// <param name="methodInfo"></param>
/// <param name="delegateInfo"></param>
/// <returns>true:可匹配。false:匹配失败</returns>
private static bool IsReturnTypeMatchable(MethodInfo methodInfo, DelegateInfo delegateInfo) ...{
The mapping logic used before .net 2.0#region The mapping logic used before .net 2.0
// if (methodInfo.ReturnType == delegateInfo.ReturnType) {
// return true;
// }
// return false;
#endregion
The current mapping logic#region The current mapping logic
if (delegateInfo.ReturnType.IsAssignableFrom(methodInfo.ReturnType)) ...{
return true;
}
return false;
#endregion
}
/**//// <summary>
/// 判断参数列表是否匹配
/// </summary>
/// <remarks>
/// 在.net 2.0之前,要求参数列表中的每个参数的类型都分别相同。
///
/// 在.net 2.0中,仅要求委托签名中声明的各参数可分别赋给方法中声明的相应参数,
/// 这样就允许前者是后者的继承类。此谓之“协变”也。
/// </remarks>
/// <param name="methodInfo"></param>
/// <param name="delegateInfo"></param>
/// <returns>true:可匹配。false:匹配失败</returns>
private static bool IsParamsMatchable(MethodInfo methodInfo, DelegateInfo delegateInfo) ...{
ParameterInfo[] methodParameterInfos = methodInfo.GetParameters();
ParameterInfo[] delegateParameterInfos = delegateInfo.GetParameters();
if (delegateParameterInfos.Length != delegateParameterInfos.Length) ...{
return false;
}
for (int i = 0; i < methodParameterInfos.Length; i++) ...{
The mapping logic used before .net 2.0#region The mapping logic used before .net 2.0
// if(methodParameterInfos[i].ParameterType != delegateParameterInfos[i].ParameterType) {
// return false;
// }
#endregion
The current mapping logic#region The current mapping logic
if (!methodParameterInfos[i].ParameterType.IsAssignableFrom(delegateParameterInfos[i].ParameterType)) ...{
return false;
}
#endregion
}
return true;
}
}
/**//// <summary>
/// 判断某方法与某委托签名是否匹配。(.net 2.0)
/// </summary>
/// <remarks>
/// DelegateInfo类可以通过Microsoft.Cci命名空间下的类构造:)
/// </remarks>
/// <param name="methodInfo"></param>
/// <param name="delegateInfo"></param>
/// <returns>true:可匹配。false:匹配失败</returns>
public static bool IsMatchable(MethodInfo methodInfo, DelegateInfo delegateInfo) ...{
return IsReturnTypeMatchable(methodInfo, delegateInfo) &&
IsParamsMatchable(methodInfo, delegateInfo);
}
/**//// <summary>
/// 判断返回类型是否可匹配
/// </summary>
/// <remarks>
/// 在.net 2.0之前,要求返回类型相同。
///
/// 在.net 2.0中,仅要求方法的返回类型可以赋给委托签名中声明的返回类型,
/// 这样就允许方法返回签名中规定类型的子类。此谓之“协变”也。
/// </remarks>
/// <param name="methodInfo"></param>
/// <param name="delegateInfo"></param>
/// <returns>true:可匹配。false:匹配失败</returns>
private static bool IsReturnTypeMatchable(MethodInfo methodInfo, DelegateInfo delegateInfo) ...{
The mapping logic used before .net 2.0#region The mapping logic used before .net 2.0
// if (methodInfo.ReturnType == delegateInfo.ReturnType) {
// return true;
// }
// return false;
#endregion
The current mapping logic#region The current mapping logic
if (delegateInfo.ReturnType.IsAssignableFrom(methodInfo.ReturnType)) ...{
return true;
}
return false;
#endregion
}
/**//// <summary>
/// 判断参数列表是否匹配
/// </summary>
/// <remarks>
/// 在.net 2.0之前,要求参数列表中的每个参数的类型都分别相同。
///
/// 在.net 2.0中,仅要求委托签名中声明的各参数可分别赋给方法中声明的相应参数,
/// 这样就允许前者是后者的继承类。此谓之“协变”也。
/// </remarks>
/// <param name="methodInfo"></param>
/// <param name="delegateInfo"></param>
/// <returns>true:可匹配。false:匹配失败</returns>
private static bool IsParamsMatchable(MethodInfo methodInfo, DelegateInfo delegateInfo) ...{
ParameterInfo[] methodParameterInfos = methodInfo.GetParameters();
ParameterInfo[] delegateParameterInfos = delegateInfo.GetParameters();
if (delegateParameterInfos.Length != delegateParameterInfos.Length) ...{
return false;
}
for (int i = 0; i < methodParameterInfos.Length; i++) ...{
The mapping logic used before .net 2.0#region The mapping logic used before .net 2.0
// if(methodParameterInfos[i].ParameterType != delegateParameterInfos[i].ParameterType) {
// return false;
// }
#endregion
The current mapping logic#region The current mapping logic
if (!methodParameterInfos[i].ParameterType.IsAssignableFrom(delegateParameterInfos[i].ParameterType)) ...{
return false;
}
#endregion
}
return true;
}
}
可以看到,与之前的版本相比,.net 2.0在匹配方法和委托签名的过程中对返回类型和参数类型不再要求严格相同,增加了对如下两种情况的许可:
1. 返回类型方面(协变)
允许方法的返回类型是委托签名中定义的返回类型的继承类。如:
public delegate object DemoDelegate();
public class Demo ...{
public static string Foo() ...{
return null;
}
public DemoDelegate demoDelegate = new DemoDelegate(Foo);
}
public class Demo ...{
public static string Foo() ...{
return null;
}
public DemoDelegate demoDelegate = new DemoDelegate(Foo);
}
2. 参数类型方面(逆变)
允许方法的参数是委托签名中对应位置上参数的基类。如:
public delegate void DemoDelegate(string s);
public class Demo ...{
public static void Foo(object obj) ...{
}
public DemoDelegate demoDelegate = new DemoDelegate(Foo);
}
public class Demo ...{
public static void Foo(object obj) ...{
}
public DemoDelegate demoDelegate = new DemoDelegate(Foo);
}
两者综合起来的例子:
public delegate object DemoDelegate(string s);
public class Demo ...{
public static string Foo(object obj) ...{
return null;
}
public DemoDelegate demoDelegate = new DemoDelegate(Foo);
}
public class Demo ...{
public static string Foo(object obj) ...{
return null;
}
public DemoDelegate demoDelegate = new DemoDelegate(Foo);
}
可能MSDN上关于这两个特性的例子只是为了从语法上描述的方便,窃以为并不是非常的合适。引入这两个特性,其本身充分迎合了软件设计中“宽进严出”的准则。但是在具体的应用中,除了作为一种代码重用的手段,我并没有找到太多的适用场景,所以一直没有认真关注过。最近在读WPF Unleashed,里面第三章提到了Contravariance,在WPF中逆变性似乎被大规模的应用,但好像也顶多只是应用的规模大而已。Google了两天,没有找到特别有意义的玩意,如果你有好的想法,还望不吝赐教。
P.S.近期偶将贴出WPF Unleashed第三章的翻译,敬请期待。可怜偶始终找不到完整版的WPF Unleashed……