#develop (short for SharpDevelop) is a free IDE for .NET programming languages.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

747 lines
26 KiB

// Copyright (c) 2010 AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.Utils;
namespace ICSharpCode.NRefactory.CSharp.Resolver
{
/// <summary>
/// Holds information about a conversion between two types.
/// </summary>
public struct Conversion : IEquatable<Conversion>
{
public static readonly Conversion None = default(Conversion);
public static readonly Conversion IdentityConversion = new Conversion(1);
public static readonly Conversion ImplicitNumericConversion = new Conversion(2);
public static readonly Conversion ImplicitEnumerationConversion = new Conversion(3);
public static readonly Conversion ImplicitNullableConversion = new Conversion(4);
public static readonly Conversion NullLiteralConversion = new Conversion(5);
public static readonly Conversion ImplicitReferenceConversion = new Conversion(6);
public static readonly Conversion BoxingConversion = new Conversion(7);
public static readonly Conversion ImplicitDynamicConversion = new Conversion(8);
public static readonly Conversion ImplicitConstantExpressionConversion = new Conversion(9);
public static readonly Conversion ImplicitTypeParameterConversion = new Conversion(10);
const int userDefinedImplicitConversionKind = 11;
public static readonly Conversion ImplicitPointerConversion = new Conversion(12);
const int anonymousFunctionConversionKind = 13;
const int methodGroupConversionKind = 14;
static readonly string[] conversionNames = {
"None",
"Identity conversion",
"Implicit numeric conversion",
"Implicit enumeration conversion",
"Implicit nullable conversion",
"Null literal conversion",
"Implicit reference conversion",
"Boxing conversion",
"Implicit dynamic conversion",
"Implicit constant expression conversion",
"Implicit conversion involving type parameter",
"User-defined implicit conversion",
"Implicit pointer conversion",
"Anonymous function conversion",
"Method group conversion",
};
public static Conversion UserDefinedImplicitConversion(IMethod operatorMethod)
{
return new Conversion(userDefinedImplicitConversionKind, operatorMethod);
}
public static Conversion MethodGroupConversion(IMethod chosenMethod)
{
return new Conversion(methodGroupConversionKind, chosenMethod);
}
/// <summary>
/// Creates a new anonymous function conversion.
/// </summary>
/// <param name="data">Used by ResolveVisitor to pass the LambdaTypeHypothesis.</param>
public static Conversion AnonymousFunctionConversion(object data)
{
return new Conversion(anonymousFunctionConversionKind, data);
}
readonly int kind;
internal readonly object data;
public Conversion(int kind, object data = null)
{
this.kind = kind;
this.data = data;
}
/// <summary>
/// Gets whether this conversion is an implicit conversion.
/// </summary>
public bool IsImplicitConversion {
get {
return kind >= IdentityConversion.kind && kind <= methodGroupConversionKind;
}
}
/// <summary>
/// Gets whether this conversion is user-defined.
/// </summary>
public bool IsUserDefined {
get { return kind == userDefinedImplicitConversionKind; }
}
/// <summary>
/// Gets whether this conversion is a method group conversion.
/// </summary>
public bool IsMethodGroupConversion {
get { return kind == methodGroupConversionKind; }
}
/// <summary>
/// Gets whether this conversion is an anonymous function conversion.
/// </summary>
public bool IsAnonymousFunctionConversion {
get { return kind == anonymousFunctionConversionKind; }
}
/// <summary>
/// Gets the method associated with this conversion.
/// For user-defined conversions, this is the method being called.
/// For method-group conversions, this is the method that was chosen from the group.
/// </summary>
public IMethod Method {
get { return data as IMethod; }
}
public override string ToString()
{
string name = conversionNames[kind];
if (data != null)
return name + " (" + data + ")";
else
return name;
}
public static implicit operator bool(Conversion conversion)
{
return conversion.kind != 0;
}
#region Equals and GetHashCode implementation
public override int GetHashCode()
{
if (data != null)
return kind ^ data.GetHashCode();
else
return kind;
}
public override bool Equals(object obj)
{
return (obj is Conversion) && Equals((Conversion)obj);
}
public bool Equals(Conversion other)
{
return this.kind == other.kind && object.Equals(this.data, other.data);
}
public static bool operator ==(Conversion lhs, Conversion rhs)
{
return lhs.Equals(rhs);
}
public static bool operator !=(Conversion lhs, Conversion rhs)
{
return !lhs.Equals(rhs);
}
#endregion
}
/// <summary>
/// Contains logic that determines whether an implicit conversion exists between two types.
/// </summary>
public class Conversions
{
readonly ITypeResolveContext context;
readonly IType objectType;
public Conversions(ITypeResolveContext context)
{
if (context == null)
throw new ArgumentNullException("context");
this.context = context;
this.objectType = KnownTypeReference.Object.Resolve(context);
this.dynamicErasure = new DynamicErasure(this);
}
#region ImplicitConversion
public Conversion ImplicitConversion(ResolveResult resolveResult, IType toType)
{
if (resolveResult == null)
throw new ArgumentNullException("resolveResult");
if (resolveResult.IsCompileTimeConstant) {
if (ImplicitEnumerationConversion(resolveResult, toType))
return Conversion.ImplicitEnumerationConversion;
if (ImplicitConstantExpressionConversion(resolveResult, toType))
return Conversion.ImplicitConstantExpressionConversion;
}
Conversion c;
c = ImplicitConversion(resolveResult.Type, toType);
if (c) return c;
c = AnonymousFunctionConversion(resolveResult, toType);
if (c) return c;
c = MethodGroupConversion(resolveResult, toType);
return c;
}
public Conversion ImplicitConversion(IType fromType, IType toType)
{
if (fromType == null)
throw new ArgumentNullException("fromType");
if (toType == null)
throw new ArgumentNullException("toType");
// C# 4.0 spec: §6.1
if (IdentityConversion(fromType, toType))
return Conversion.IdentityConversion;
if (ImplicitNumericConversion(fromType, toType))
return Conversion.ImplicitNumericConversion;
if (ImplicitNullableConversion(fromType, toType))
return Conversion.ImplicitNullableConversion;
if (NullLiteralConversion(fromType, toType))
return Conversion.NullLiteralConversion;
if (ImplicitReferenceConversion(fromType, toType))
return Conversion.ImplicitReferenceConversion;
if (BoxingConversion(fromType, toType))
return Conversion.BoxingConversion;
if (ImplicitDynamicConversion(fromType, toType))
return Conversion.ImplicitDynamicConversion;
if (ImplicitTypeParameterConversion(fromType, toType))
return Conversion.ImplicitTypeParameterConversion;
if (ImplicitPointerConversion(fromType, toType))
return Conversion.ImplicitPointerConversion;
return UserDefinedImplicitConversion(fromType, toType);
}
public Conversion StandardImplicitConversion(IType fromType, IType toType)
{
if (fromType == null)
throw new ArgumentNullException("fromType");
if (toType == null)
throw new ArgumentNullException("toType");
// C# 4.0 spec: §6.3.1
if (IdentityConversion(fromType, toType))
return Conversion.IdentityConversion;
if (ImplicitNumericConversion(fromType, toType))
return Conversion.ImplicitNumericConversion;
if (ImplicitNullableConversion(fromType, toType))
return Conversion.ImplicitNullableConversion;
if (ImplicitReferenceConversion(fromType, toType))
return Conversion.ImplicitReferenceConversion;
if (ImplicitTypeParameterConversion(fromType, toType))
return Conversion.ImplicitTypeParameterConversion;
if (BoxingConversion(fromType, toType))
return Conversion.BoxingConversion;
return Conversion.None;
}
#endregion
#region IdentityConversion
/// <summary>
/// Gets whether there is an identity conversion from <paramref name="fromType"/> to <paramref name="toType"/>
/// </summary>
public bool IdentityConversion(IType fromType, IType toType)
{
// C# 4.0 spec: §6.1.1
return fromType.AcceptVisitor(dynamicErasure).Equals(toType.AcceptVisitor(dynamicErasure));
}
readonly DynamicErasure dynamicErasure;
sealed class DynamicErasure : TypeVisitor
{
readonly IType objectType;
public DynamicErasure(Conversions conversions)
{
this.objectType = conversions.objectType;
}
public override IType VisitOtherType(IType type)
{
if (type == SharedTypes.Dynamic)
return objectType;
else
return base.VisitOtherType(type);
}
}
#endregion
#region ImplicitNumericConversion
static readonly bool[,] implicitNumericConversionLookup = {
// to: short ushort int uint long ulong
// from:
/* char */ { false, true , true , true , true , true },
/* sbyte */ { true , false, true , false, true , false },
/* byte */ { true , true , true , true , true , true },
/* short */ { false, false, true , false, true , false },
/* ushort */ { false, false, true , true , true , true },
/* int */ { false, false, false, false, true , false },
/* uint */ { false, false, false, false, true , true },
};
bool ImplicitNumericConversion(IType fromType, IType toType)
{
// C# 4.0 spec: §6.1.2
TypeCode from = ReflectionHelper.GetTypeCode(fromType);
TypeCode to = ReflectionHelper.GetTypeCode(toType);
if (to >= TypeCode.Single && to <= TypeCode.Decimal) {
// Conversions to float/double/decimal exist from all integral types,
// and there's a conversion from float to double.
return from >= TypeCode.Char && from <= TypeCode.UInt64
|| from == TypeCode.Single && to == TypeCode.Double;
} else {
// Conversions to integral types: look at the table
return from >= TypeCode.Char && from <= TypeCode.UInt32
&& to >= TypeCode.Int16 && to <= TypeCode.UInt64
&& implicitNumericConversionLookup[from - TypeCode.Char, to - TypeCode.Int16];
}
}
#endregion
#region ImplicitEnumerationConversion
bool ImplicitEnumerationConversion(ResolveResult rr, IType toType)
{
// C# 4.0 spec: §6.1.3
TypeCode constantType = ReflectionHelper.GetTypeCode(rr.Type);
if (constantType >= TypeCode.SByte && constantType <= TypeCode.Decimal && Convert.ToDouble(rr.ConstantValue) == 0) {
return NullableType.GetUnderlyingType(toType).IsEnum();
}
return false;
}
#endregion
#region ImplicitNullableConversion
bool ImplicitNullableConversion(IType fromType, IType toType)
{
// C# 4.0 spec: §6.1.4
if (NullableType.IsNullable(toType)) {
IType t = NullableType.GetUnderlyingType(toType);
IType s = NullableType.GetUnderlyingType(fromType); // might or might not be nullable
return IdentityConversion(s, t) || ImplicitNumericConversion(s, t);
} else {
return false;
}
}
#endregion
#region NullLiteralConversion
bool NullLiteralConversion(IType fromType, IType toType)
{
// C# 4.0 spec: §6.1.5
if (SharedTypes.Null.Equals(fromType)) {
return NullableType.IsNullable(toType) || toType.IsReferenceType(context) == true;
} else {
return false;
}
}
#endregion
#region ImplicitReferenceConversion
bool ImplicitReferenceConversion(IType fromType, IType toType)
{
// C# 4.0 spec: §6.1.6
// reference conversions are possible only if both types are known to be reference types
if (!(fromType.IsReferenceType(context) == true && toType.IsReferenceType(context) == true))
return false;
ArrayType fromArray = fromType as ArrayType;
if (fromArray != null) {
ArrayType toArray = toType as ArrayType;
if (toArray != null) {
// array covariance (the broken kind)
return fromArray.Dimensions == toArray.Dimensions
&& ImplicitReferenceConversion(fromArray.ElementType, toArray.ElementType);
}
// conversion from single-dimensional array S[] to IList<T>:
ParameterizedType toPT = toType as ParameterizedType;
if (fromArray.Dimensions == 1 && toPT != null && toPT.TypeArguments.Count == 1
&& toPT.Namespace == "System.Collections.Generic"
&& (toPT.Name == "IList" || toPT.Name == "ICollection" || toPT.Name == "IEnumerable"))
{
// array covariance plays a part here as well (string[] is IList<object>)
return IdentityConversion(fromArray.ElementType, toPT.TypeArguments[0])
|| ImplicitReferenceConversion(fromArray.ElementType, toPT.TypeArguments[0]);
}
// conversion from any array to System.Array and the interfaces it implements:
ITypeDefinition systemArray = context.GetTypeDefinition("System", "Array", 0, StringComparer.Ordinal);
return systemArray != null && (systemArray.Equals(toType) || ImplicitReferenceConversion(systemArray, toType));
}
// now comes the hard part: traverse the inheritance chain and figure out generics+variance
return IsSubtypeOf(fromType, toType);
}
// Determines whether s is a subtype of t.
// Helper method used for ImplicitReferenceConversion, BoxingConversion and ImplicitTypeParameterConversion
bool IsSubtypeOf(IType s, IType t)
{
// conversion to dynamic + object are always possible
if (t.Equals(SharedTypes.Dynamic) || t.Equals(objectType))
return true;
// let GetAllBaseTypes do the work for us
foreach (IType baseType in s.GetAllBaseTypes(context)) {
if (IdentityOrVarianceConversion(baseType, t))
return true;
}
return false;
}
bool IdentityOrVarianceConversion(IType s, IType t)
{
ITypeDefinition def = s.GetDefinition();
if (def != null && def.Equals(t.GetDefinition())) {
ParameterizedType ps = s as ParameterizedType;
ParameterizedType pt = t as ParameterizedType;
if (ps != null && pt != null
&& ps.TypeArguments.Count == pt.TypeArguments.Count
&& ps.TypeArguments.Count == def.TypeParameters.Count)
{
// C# 4.0 spec: §13.1.3.2 Variance Conversion
for (int i = 0; i < def.TypeParameters.Count; i++) {
IType si = ps.TypeArguments[i];
IType ti = pt.TypeArguments[i];
if (IdentityConversion(si, ti))
continue;
ITypeParameter xi = def.TypeParameters[i];
switch (xi.Variance) {
case VarianceModifier.Covariant:
if (!ImplicitReferenceConversion(si, ti))
return false;
break;
case VarianceModifier.Contravariant:
if (!ImplicitReferenceConversion(ti, si))
return false;
break;
default:
return false;
}
}
} else if (ps != null || pt != null) {
return false; // only of of them is parameterized, or counts don't match? -> not valid conversion
}
return true;
}
return false;
}
#endregion
#region BoxingConversion
bool BoxingConversion(IType fromType, IType toType)
{
// C# 4.0 spec: §6.1.7
fromType = NullableType.GetUnderlyingType(fromType);
return fromType.IsReferenceType(context) == false && toType.IsReferenceType(context) == true && IsSubtypeOf(fromType, toType);
}
#endregion
#region ImplicitDynamicConversion
bool ImplicitDynamicConversion(IType fromType, IType toType)
{
// C# 4.0 spec: §6.1.8
return SharedTypes.Dynamic.Equals(fromType);
}
#endregion
#region ImplicitConstantExpressionConversion
bool ImplicitConstantExpressionConversion(ResolveResult rr, IType toType)
{
// C# 4.0 spec: §6.1.9
TypeCode fromTypeCode = ReflectionHelper.GetTypeCode(rr.Type);
TypeCode toTypeCode = ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(toType));
if (fromTypeCode == TypeCode.Int64) {
long val = (long)rr.ConstantValue;
return val >= 0 && toTypeCode == TypeCode.UInt64;
} else if (fromTypeCode == TypeCode.Int32) {
int val = (int)rr.ConstantValue;
switch (toTypeCode) {
case TypeCode.SByte:
return val >= SByte.MinValue && val <= SByte.MaxValue;
case TypeCode.Byte:
return val >= Byte.MinValue && val <= Byte.MaxValue;
case TypeCode.Int16:
return val >= Int16.MinValue && val <= Int16.MaxValue;
case TypeCode.UInt16:
return val >= UInt16.MinValue && val <= UInt16.MaxValue;
case TypeCode.UInt32:
return val >= 0;
case TypeCode.UInt64:
return val >= 0;
}
}
return false;
}
#endregion
#region ImplicitTypeParameterConversion
/// <summary>
/// Implicit conversions involving type parameters.
/// </summary>
bool ImplicitTypeParameterConversion(IType fromType, IType toType)
{
ITypeParameter t = fromType as ITypeParameter;
if (t == null)
return false; // not a type parameter
if (t.IsReferenceType(context) == true)
return false; // already handled by ImplicitReferenceConversion
return IsSubtypeOf(t, toType);
}
#endregion
#region ImplicitPointerConversion
bool ImplicitPointerConversion(IType fromType, IType toType)
{
// C# 4.0 spec: §18.4 Pointer conversions
if (fromType is PointerType && toType is PointerType && toType.ReflectionName == "System.Void*")
return true;
if (SharedTypes.Null.Equals(fromType) && toType is PointerType)
return true;
return false;
}
#endregion
#region UserDefinedImplicitConversion
Conversion UserDefinedImplicitConversion(IType fromType, IType toType)
{
// C# 4.0 spec §6.4.4 User-defined implicit conversions
// Currently we only test whether an applicable implicit conversion exists,
// we do not resolve which conversion is the most specific and gets used.
// Find the candidate operators:
Predicate<IMethod> opImplicitFilter = m => m.IsStatic && m.IsOperator && m.Name == "op_Implicit" && m.Parameters.Count == 1;
var operators = NullableType.GetUnderlyingType(fromType).GetMethods(context, opImplicitFilter)
.Concat(NullableType.GetUnderlyingType(toType).GetMethods(context, opImplicitFilter));
// Determine whether one of them is applicable:
foreach (IMethod op in operators) {
IType sourceType = op.Parameters[0].Type.Resolve(context);
IType targetType = op.ReturnType.Resolve(context);
// Try if the operator is applicable:
if (StandardImplicitConversion(fromType, sourceType) && StandardImplicitConversion(targetType, toType)) {
return Conversion.UserDefinedImplicitConversion(op);
}
// Try if the operator is applicable in lifted form:
if (sourceType.IsReferenceType(context) == false && targetType.IsReferenceType(context) == false) {
IType liftedSourceType = NullableType.Create(sourceType, context);
IType liftedTargetType = NullableType.Create(targetType, context);
if (StandardImplicitConversion(fromType, liftedSourceType) && StandardImplicitConversion(liftedTargetType, toType)) {
return Conversion.UserDefinedImplicitConversion(op);
}
}
}
return Conversion.None;
}
#endregion
#region AnonymousFunctionConversion
Conversion AnonymousFunctionConversion(ResolveResult resolveResult, IType toType)
{
// C# 4.0 spec §6.5 Anonymous function conversions
LambdaResolveResult f = resolveResult as LambdaResolveResult;
if (f == null)
return Conversion.None;
if (!f.IsAnonymousMethod) {
// It's a lambda, so conversions to expression trees exist
// (even if the conversion leads to a compile-time error, e.g. for statement lambdas)
toType = UnpackExpressionTreeType(toType);
}
IMethod d = toType.GetDelegateInvokeMethod();
if (d == null)
return Conversion.None;
IType[] dParamTypes = new IType[d.Parameters.Count];
for (int i = 0; i < dParamTypes.Length; i++) {
dParamTypes[i] = d.Parameters[i].Type.Resolve(context);
}
IType dReturnType = d.ReturnType.Resolve(context);
if (f.HasParameterList) {
// If F contains an anonymous-function-signature, then D and F have the same number of parameters.
if (d.Parameters.Count != f.Parameters.Count)
return Conversion.None;
if (f.IsImplicitlyTyped) {
// If F has an implicitly typed parameter list, D has no ref or out parameters.
foreach (IParameter p in d.Parameters) {
if (p.IsOut || p.IsRef)
return Conversion.None;
}
} else {
// If F has an explicitly typed parameter list, each parameter in D has the same type
// and modifiers as the corresponding parameter in F.
for (int i = 0; i < f.Parameters.Count; i++) {
IParameter pD = d.Parameters[i];
IParameter pF = f.Parameters[i];
if (pD.IsRef != pF.IsRef || pD.IsOut != pF.IsOut)
return Conversion.None;
if (!dParamTypes[i].Equals(pF.Type.Resolve(context)))
return Conversion.None;
}
}
} else {
// If F does not contain an anonymous-function-signature, then D may have zero or more parameters of any
// type, as long as no parameter of D has the out parameter modifier.
foreach (IParameter p in d.Parameters) {
if (p.IsOut)
return Conversion.None;
}
}
return f.IsValid(dParamTypes, dReturnType, this);
}
static IType UnpackExpressionTreeType(IType type)
{
ParameterizedType pt = type as ParameterizedType;
if (pt != null && pt.TypeParameterCount == 1 && pt.Name == "Expression" && pt.Namespace == "System.Linq.Expressions") {
return pt.TypeArguments[0];
} else {
return type;
}
}
#endregion
#region MethodGroupConversion
Conversion MethodGroupConversion(ResolveResult resolveResult, IType toType)
{
// C# 4.0 spec §6.6 Method group conversions
MethodGroupResolveResult rr = resolveResult as MethodGroupResolveResult;
if (rr == null)
return Conversion.None;
IMethod m = toType.GetDelegateInvokeMethod();
if (m == null)
return Conversion.None;
ResolveResult[] args = new ResolveResult[m.Parameters.Count];
for (int i = 0; i < args.Length; i++) {
IParameter param = m.Parameters[i];
IType parameterType = param.Type.Resolve(context);
if ((param.IsRef || param.IsOut) && parameterType.Kind == TypeKind.ByReference) {
parameterType = ((ByReferenceType)parameterType).ElementType;
args[i] = new ByReferenceResolveResult(parameterType, param.IsOut);
} else {
args[i] = new ResolveResult(parameterType);
}
}
var or = rr.PerformOverloadResolution(context, args, allowExpandingParams: false);
if (or.FoundApplicableCandidate)
return Conversion.MethodGroupConversion((IMethod)or.BestCandidate);
else
return Conversion.None;
}
#endregion
#region BetterConversion
/// <summary>
/// Gets the better conversion (C# 4.0 spec, §7.5.3.3)
/// </summary>
/// <returns>0 = neither is better; 1 = t1 is better; 2 = t2 is better</returns>
public int BetterConversion(ResolveResult resolveResult, IType t1, IType t2)
{
LambdaResolveResult lambda = resolveResult as LambdaResolveResult;
if (lambda != null) {
if (!lambda.IsAnonymousMethod) {
t1 = UnpackExpressionTreeType(t1);
t2 = UnpackExpressionTreeType(t2);
}
IMethod m1 = t1.GetDelegateInvokeMethod();
IMethod m2 = t2.GetDelegateInvokeMethod();
if (m1 == null || m2 == null)
return 0;
int r = BetterConversionTarget(t1, t2);
if (r != 0)
return r;
if (m1.Parameters.Count != m2.Parameters.Count)
return 0;
IType[] parameterTypes = new IType[m1.Parameters.Count];
for (int i = 0; i < parameterTypes.Length; i++) {
parameterTypes[i] = m1.Parameters[i].Type.Resolve(context);
if (!parameterTypes[i].Equals(m2.Parameters[i].Type.Resolve(context)))
return 0;
}
if (lambda.HasParameterList && parameterTypes.Length != lambda.Parameters.Count)
return 0;
IType ret1 = m1.ReturnType.Resolve(context);
IType ret2 = m2.ReturnType.Resolve(context);
if (ret1.Kind == TypeKind.Void && ret2.Kind != TypeKind.Void)
return 1;
if (ret1.Kind != TypeKind.Void && ret2.Kind == TypeKind.Void)
return 2;
IType inferredRet = lambda.GetInferredReturnType(parameterTypes);
return BetterConversion(inferredRet, ret1, ret2);
} else {
return BetterConversion(resolveResult.Type, t1, t2);
}
}
/// <summary>
/// Gets the better conversion (C# 4.0 spec, §7.5.3.4)
/// </summary>
/// <returns>0 = neither is better; 1 = t1 is better; 2 = t2 is better</returns>
public int BetterConversion(IType s, IType t1, IType t2)
{
bool ident1 = IdentityConversion(s, t1);
bool ident2 = IdentityConversion(s, t2);
if (ident1 && !ident2)
return 1;
if (ident2 && !ident1)
return 2;
return BetterConversionTarget(t1, t2);
}
/// <summary>
/// Gets the better conversion target (C# 4.0 spec, §7.5.3.5)
/// </summary>
/// <returns>0 = neither is better; 1 = t1 is better; 2 = t2 is better</returns>
int BetterConversionTarget(IType t1, IType t2)
{
bool t1To2 = ImplicitConversion(t1, t2);
bool t2To1 = ImplicitConversion(t2, t1);
if (t1To2 && !t2To1)
return 1;
if (t2To1 && !t1To2)
return 2;
TypeCode t1Code = ReflectionHelper.GetTypeCode(t1);
TypeCode t2Code = ReflectionHelper.GetTypeCode(t2);
if (IsBetterIntegralType(t1Code, t2Code))
return 1;
if (IsBetterIntegralType(t2Code, t1Code))
return 2;
return 0;
}
bool IsBetterIntegralType(TypeCode t1, TypeCode t2)
{
// signed types are better than unsigned types
switch (t1) {
case TypeCode.SByte:
return t2 == TypeCode.Byte || t2 == TypeCode.UInt16 || t2 == TypeCode.UInt32 || t2 == TypeCode.UInt64;
case TypeCode.Int16:
return t2 == TypeCode.UInt16 || t2 == TypeCode.UInt32 || t2 == TypeCode.UInt64;
case TypeCode.Int32:
return t2 == TypeCode.UInt32 || t2 == TypeCode.UInt64;
case TypeCode.Int64:
return t2 == TypeCode.UInt64;
default:
return false;
}
}
#endregion
}
}