📄 memberlookuphelper.cs
字号:
break;
if (!InferTypeArgument(parameters[i].ReturnType, arguments[i], result)) {
// inferring failed: maybe this is a params parameter that must be expanded?
if (parameters[i].IsParams && parameters[i].ReturnType.ArrayDimensions == 1) {
InferTypeArgument(parameters[i].ReturnType.ArrayElementType, arguments[i], result);
}
}
}
// only return the result array when there something was inferred
for (int i = 0; i < result.Length; i++) {
if (result[i] != null) {
return result;
}
}
return null;
}
public static bool InferTypeArgument(IReturnType expectedArgument, IReturnType passedArgument, IReturnType[] outputArray)
{
if (passedArgument == null) return true; // TODO: NullTypeReference
if (expectedArgument != null && expectedArgument.ArrayDimensions > 0) {
if (expectedArgument.ArrayDimensions == passedArgument.ArrayDimensions) {
return InferTypeArgument(expectedArgument.ArrayElementType, passedArgument.ArrayElementType, outputArray);
} else if (passedArgument.TypeArguments != null) {
switch (passedArgument.FullyQualifiedName) {
case "System.Collections.Generic.IList":
case "System.Collections.Generic.ICollection":
case "System.Collections.Generic.IEnumerable":
return InferTypeArgument(expectedArgument.ArrayElementType, passedArgument.TypeArguments[0], outputArray);
}
}
// If P is an array type, and A is not an array type of the same rank,
// or an instantiation of IList<>, ICollection<>, or IEnumerable<>, then
// type inference fails for the generic method.
return false;
}
GenericReturnType methodTP = expectedArgument as GenericReturnType;
if (methodTP != null && methodTP.TypeParameter.Method != null) {
if (methodTP.TypeParameter.Index < outputArray.Length) {
outputArray[methodTP.TypeParameter.Index] = passedArgument;
}
return true;
}
if (expectedArgument.TypeArguments != null) {
// The spec for this case is quite complex.
// For our purposes, we can simplify enourmously:
if (passedArgument.TypeArguments == null) return false;
int count = Math.Min(expectedArgument.TypeArguments.Count, passedArgument.TypeArguments.Count);
for (int i = 0; i < count; i++) {
InferTypeArgument(expectedArgument.TypeArguments[i], passedArgument.TypeArguments[i], outputArray);
}
}
return true;
}
#endregion
#region IsApplicable
static bool IsApplicable(IList<IParameter> parameters,
IReturnType[] arguments,
bool allowAdditionalArguments,
out int score,
out bool expanded)
{
// see ECMA-334, § 14.4.2.1
// TODO: recognize ref/out (needs info about passing mode for arguments, you have to introduce RefReturnType)
expanded = false;
score = 0;
if (parameters.Count == 0)
return arguments.Length == 0;
if (!allowAdditionalArguments && parameters.Count > arguments.Length + 1)
return false;
int lastParameter = parameters.Count - 1;
// check all arguments except the last
bool ok = true;
for (int i = 0; i < Math.Min(lastParameter, arguments.Length); i++) {
if (IsApplicable(arguments[i], parameters[i].ReturnType)) {
score++;
} else {
ok = false;
}
}
if (!ok) {
return false;
}
if (parameters.Count == arguments.Length) {
// try if method is applicable in normal form by checking last argument
if (IsApplicable(arguments[lastParameter], parameters[lastParameter].ReturnType)) {
return true;
}
}
// method is not applicable in normal form, try expanded form:
// - last parameter must be params array
if (!parameters[lastParameter].IsParams) {
return false;
}
expanded = true;
score++;
// - all additional parameters must be applicable to the unpacked array
IReturnType rt = parameters[lastParameter].ReturnType;
if (rt == null || rt.ArrayDimensions == 0) {
return false;
}
for (int i = lastParameter; i < arguments.Length; i++) {
if (IsApplicable(arguments[i], rt.ArrayElementType)) {
score++;
} else {
ok = false;
}
}
return ok;
}
static bool IsApplicable(IReturnType argument, IReturnType expected)
{
if (argument == null) // TODO: Use NullReturnType instead of no return type
return true; // "null" can be passed for any argument
if (expected is GenericReturnType) {
foreach (IReturnType constraint in ((GenericReturnType)expected).TypeParameter.Constraints) {
if (!ConversionExists(argument, constraint)) {
return false;
}
}
}
return ConversionExists(argument, expected);
}
#endregion
#region Conversion exists
/// <summary>
/// Checks if an implicit conversion exists from <paramref name="from"/> to <paramref name="to"/>.
/// </summary>
public static bool ConversionExists(IReturnType from, IReturnType to)
{
// ECMA-334, § 13.1 Implicit conversions
// Identity conversion:
if (from == to) return true;
if (from == null || to == null) return false;
if (from.Equals(to)) {
return true;
}
bool fromIsDefault = from.IsDefaultReturnType;
bool toIsDefault = to.IsDefaultReturnType;
if (fromIsDefault && toIsDefault) {
// Implicit numeric conversions:
int f = GetPrimitiveType(from);
int t = GetPrimitiveType(to);
if (f == SByte && (t == Short || t == Int || t == Long || t == Float || t == Double || t == Decimal))
return true;
if (f == Byte && (t == Short || t == UShort || t == Int || t == UInt || t == Long || t == ULong || t == Float || t == Double || t == Decimal))
return true;
if (f == Short && (t == Int || t == Long || t == Float || t == Double || t == Decimal))
return true;
if (f == UShort && (t == Int || t == UInt || t == Long || t == ULong || t == Float || t == Double || t == Decimal))
return true;
if (f == Int && (t == Long || t == Float || t == Double || t == Decimal))
return true;
if (f == UInt && (t == Long || t == ULong || t == Float || t == Double || t == Decimal))
return true;
if ((f == Long || f == ULong) && (t == Float || t == Double || t == Decimal))
return true;
if (f == Char && (t == UShort || t == Int || t == UInt || t == Long || t == ULong || t == Float || t == Double || t == Decimal))
return true;
if (f == Float && t == Double)
return true;
}
// Implicit reference conversions:
if (toIsDefault && to.FullyQualifiedName == "System.Object") {
return true; // from any type to object
}
if (toIsDefault && (fromIsDefault || from.ArrayDimensions > 0)) {
IClass c1 = from.GetUnderlyingClass();
IClass c2 = to.GetUnderlyingClass();
if (c1 != null && c1.IsTypeInInheritanceTree(c2)) {
return true;
}
}
if (from.ArrayDimensions > 0 && from.ArrayDimensions == to.ArrayDimensions) {
// from array to other array type
return ConversionExists(from.ArrayElementType, to.ArrayElementType);
}
IList<IReturnType> fromTypeArguments = from.TypeArguments;
IList<IReturnType> toTypeArguments = to.TypeArguments;
if (fromTypeArguments != null && toTypeArguments != null) {
if (from.FullyQualifiedName == to.FullyQualifiedName) {
if (fromTypeArguments.Count == toTypeArguments.Count) {
for (int i = 0; i < fromTypeArguments.Count; i++) {
if (fromTypeArguments[i] == toTypeArguments[i])
continue;
if (object.Equals(fromTypeArguments[i], toTypeArguments[i]))
continue;
if (!(toTypeArguments[i] is GenericReturnType))
return false;
}
return true;
}
}
}
return false;
}
#endregion
#region Better conversion
/// <summary>
/// Gets if the conversion from <paramref name="from"/> to <paramref name="to1"/> is better than
/// the conversion from <paramref name="from"/> to <paramref name="to2"/>.
/// </summary>
/// <returns>
/// 0 = neither conversion is better<br/>
/// 1 = from -> to1 is the better conversion<br/>
/// 2 = from -> to2 is the better conversion.
/// </returns>
public static int GetBetterConversion(IReturnType from, IReturnType to1, IReturnType to2)
{
if (from == null) return 0;
if (to1 == null) return 2;
if (to2 == null) return 1;
// See ECMA-334, § 14.4.2.3
// If T1 and T2 are the same type, neither conversion is better.
if (to1.Equals(to2)) {
return 0;
}
// If S is T1, C1 is the better conversion.
if (from.Equals(to1)) {
return 1;
}
// If S is T2, C2 is the better conversion.
if (from.Equals(to2)) {
return 2;
}
bool canConvertFrom1To2 = ConversionExists(to1, to2);
bool canConvertFrom2To1 = ConversionExists(to2, to1);
// If an implicit conversion from T1 to T2 exists, and no implicit conversion
// from T2 to T1 exists, C1 is the better conversion.
if (canConvertFrom1To2 && !canConvertFrom2To1) {
return 1;
}
// If an implicit conversion from T2 to T1 exists, and no implicit conversion
// from T1 to T2 exists, C2 is the better conversion.
if (canConvertFrom2To1 && !canConvertFrom1To2) {
return 2;
}
if (to1.IsDefaultReturnType && to2.IsDefaultReturnType) {
return GetBetterPrimitiveConversion(to1, to2);
}
// Otherwise, neither conversion is better.
return 0;
}
const int Byte = 1;
const int Short = 2;
const int Int = 3;
const int Long = 4;
const int SByte = 5;
const int UShort = 6;
const int UInt = 7;
const int ULong = 8;
const int Float = 9;
const int Double = 10;
const int Char = 11;
const int Decimal= 12;
static int GetBetterPrimitiveConversion(IReturnType to1, IReturnType to2)
{
int t1 = GetPrimitiveType(to1);
int t2 = GetPrimitiveType(to2);
if (t1 == 0 || t2 == 0) return 0; // not primitive
if (t1 == SByte && (t2 == Byte || t2 == UShort || t2 == UInt || t2 == ULong))
return 1;
if (t2 == SByte && (t1 == Byte || t1 == UShort || t1 == UInt || t1 == ULong))
return 2;
if (t1 == Short && (t2 == UShort || t2 == UInt || t2 == ULong))
return 1;
if (t2 == Short && (t1 == UShort || t1 == UInt || t1 == ULong))
return 2;
if (t1 == Int && (t2 == UInt || t2 == ULong))
return 1;
if (t2 == Int && (t1 == UInt || t1 == ULong))
return 2;
if (t1 == Long && t2 == ULong)
return 1;
if (t2 == Long && t1 == ULong)
return 2;
return 0;
}
static int GetPrimitiveType(IReturnType t)
{
switch (t.FullyQualifiedName) {
case "System.SByte": return SByte;
case "System.Byte": return Byte;
case "System.Int16": return Short;
case "System.UInt16": return UShort;
case "System.Int32": return Int;
case "System.UInt32": return UInt;
case "System.Int64": return Long;
case "System.UInt64": return ULong;
case "System.Single": return Float;
case "System.Double": return Double;
case "System.Char": return Char;
case "System.Decimal": return Decimal;
default: return 0;
}
}
#endregion
/// <summary>
/// Gets the common base type of a and b.
/// </summary>
public static IReturnType GetCommonType(IReturnType a, IReturnType b)
{
if (a == null) return b;
if (b == null) return a;
if (ConversionExists(a, b))
return b;
if (ConversionExists(b, a))
return a;
IClass c = a.GetUnderlyingClass();
if (c != null) {
foreach (IClass baseClass in c.ClassInheritanceTree) {
IReturnType baseType = baseClass.DefaultReturnType;
if (baseClass.TypeParameters.Count > 0) {
IReturnType[] typeArguments = new IReturnType[baseClass.TypeParameters.Count];
for (int i = 0; i < typeArguments.Length; i++) {
typeArguments[i] = GetTypeParameterPassedToBaseClass(a, baseClass, i);
}
baseType = new ConstructedReturnType(baseType, typeArguments);
}
if (ConversionExists(b, baseType))
return baseType;
}
}
return ReflectionReturnType.Object;
}
/// <summary>
/// Gets the type parameter that was passed to a certain base class.
/// For example, when <paramref name="returnType"/> is Dictionary(of string, int)
/// this method will return KeyValuePair(of string, int)
/// </summary>
public static IReturnType GetTypeParameterPassedToBaseClass(IReturnType returnType, IClass baseClass, int baseClassTypeParameterIndex)
{
IClass c = returnType.GetUnderlyingClass();
if (c == null) return null;
if (baseClass.CompareTo(c) == 0) {
if (returnType.TypeArguments == null || baseClassTypeParameterIndex >= returnType.TypeArguments.Count)
return null;
return returnType.TypeArguments[baseClassTypeParameterIndex];
}
foreach (IReturnType baseType in c.BaseTypes) {
if (baseClass.CompareTo(baseType.GetUnderlyingClass()) == 0) {
if (baseType.TypeArguments == null || baseClassTypeParameterIndex >= baseType.TypeArguments.Count)
return null;
IReturnType result = baseType.TypeArguments[baseClassTypeParameterIndex];
if (returnType.TypeArguments != null) {
result = ConstructedReturnType.TranslateType(result, returnType.TypeArguments, false);
}
return result;
}
}
return null;
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -