2015-08-15 106 views
19

我有兩種類型,T和U,我想知道的隱式轉換操作符是否在T定義爲U.如何確定隱式轉換是否存在於C#中?

我所知道的IsAssignableFrom存在的,這是不是我有什麼尋找,因爲它不涉及隱式演員。

google搜索的一點使我this solution,但在作者自己的話說,這是醜陋的代碼(它試圖隱式轉換,如果有一個例外,否則真返回false ...)

看來測試對於具有正確簽名的op_Implicit方法的存在won't work for primitive types

是否有一種更清晰的方式來確定隱式投射算子的存在?

+0

只是一個提示:看看[隱式類型轉換運算符](https://msdn.microsoft.com/en-us/library/z5z9kes2.aspx)。我想應該有一種方法來通過反思找到隱式操作符... – elgonzo

+0

您能否詳細談談您試圖用結果完成什麼目標?我過去遇到類似的問題,意識到它幾乎必須看起來像什麼CliveDM [鏈接](https://github.com/CYJB/Cyjb/blob/f4424c4f81cacd09e9ce5d202a03b0c121c09ac2/Cyjb/TypeExt.cs)[下面](http://stackoverflow.com/a/32025388/1083771),並決定只調用[Convert.ChangeType](https://msdn.microsoft.com/en-us/library/dtb69x08.aspx)並處理例外。我意識到這可能不是一個可行的解決方案,但也許有類似的解決方法。 –

回答

2

Here's我找到的解決方案。顯示爲波紋管(經過一些簡單的翻譯)的主要代碼:

public static bool IsImplicitFrom(this Type type, Type fromType) { 
    if (type == null || fromType == null) { 
     return false; 
    } 

    // support for reference type 
    if (type.IsByRef) { type = type.GetElementType(); } 
    if (fromType.IsByRef) { fromType = type.GetElementType(); } 

    // could always be convert to object 
    if (type.Equals(typeof(object))) { 
     return true; 
    } 

    // check if it could be convert using standard implicit cast 
    if (IsStandardImplicitFrom(type, fromType)) { 
     return true; 
    } 

    // determine implicit convert operator 
    Type nonNullalbeType, nonNullableFromType; 
    if (IsNullableType(type, out nonNullalbeType) && 
     IsNullableType(fromType, out nonNullableFromType)) { 
     type = nonNullalbeType; 
     fromType = nonNullableFromType; 
    } 

    return ConversionCache.GetImplicitConversion(fromType, type) != null; 
} 

internal static bool IsStandardImplicitFrom(this Type type, Type fromType) { 
    // support for Nullable<T> 
    if (!type.IsValueType || IsNullableType(ref type)) { 
     fromType = GetNonNullableType(fromType); 
    } 

    // determine implicit value type convert 
    HashSet<TypeCode> typeSet; 
    if (!type.IsEnum && 
     ImplicitNumericConversions.TryGetValue(Type.GetTypeCode(type), out typeSet)) { 
     if (!fromType.IsEnum && typeSet.Contains(Type.GetTypeCode(fromType))) { 
      return true; 
     } 
    } 

    // determine implicit reference type convert and boxing convert 
    return type.IsAssignableFrom(fromType); 
} 

更新: Here's整個文件。

+1

此代碼似乎相當不完整。 – poke

+1

@poke我更新了我的答案,希望它可以幫助。 –

+0

'if(fromType.IsByRef){fromType = type.GetElementType(); }在錯誤的變量上調用'GetElementType'。 –

14

你可以使用反射來找到目標類型的隱式轉換方法:

public static bool HasImplicitConversion(Type baseType, Type targetType) 
{ 
    return baseType.GetMethods(BindingFlags.Public | BindingFlags.Static) 
     .Where(mi => mi.Name == "op_Implicit" && mi.ReturnType == targetType) 
     .Any(mi => { 
      ParameterInfo pi = mi.GetParameters().FirstOrDefault(); 
      return pi != null && pi.ParameterType == baseType; 
     }); 
} 

您可以使用它像這樣:

class X {} 
class Y 
{ 
    public static implicit operator X (Y y) 
    { 
     return new X(); 
    } 

    public static implicit operator Y (X x) 
    { 
     return new Y(); 
    } 
} 

// and then: 
bool conversionExists = HasImplicitConversion(typeof(Y), typeof(X)); 

請注意,這只是檢查是否存在隱式類型轉換基類型(第一個傳入的類型)。從技術上講,類型轉換也可以在其他類型上定義,所以您可能需要再次調用類型轉換(或將其構建到方法中)。隱式類型轉換可能不存在於這兩種類型。

+0

它似乎不適用於原始類型,例如MappingsGetter.HasImplicitConversion(typeof(int),typeof(decimal))返回false,而存在隱式轉換:https:// msdn.microsoft。com/en-us/library/y5b434w4.aspx – Brann

+3

@Brann這些類型沒有隱式操作符;類型轉換由CLR直接完成。國家執行['IConvertible'(https://msdn.microsoft.com/en-us/library/system.iconvertible.aspx),因此,您可以測試這一點。 – poke

+0

@Brann對於原始類型,你必須在某處硬編碼一個表。我不知道任何內置機制,這將允許您以編程方式執行此操作。 – Kyle

3

我最終手動處理原始類型場景。不是很優雅,但它的作品。

我還添加了額外的邏輯來處理可爲空的類型和枚舉。

我爲用戶定義的類型場景重用Poke's code

public class AvailableCastChecker 
{ 
    public static bool CanCast(Type from, Type to) 
    { 
     if (from.IsAssignableFrom(to)) 
     { 
      return true; 
     } 
     if (HasImplicitConversion(from, from, to)|| HasImplicitConversion(to, from, to)) 
     { 
      return true; 
     } 
     List<Type> list; 
     if (ImplicitNumericConversions.TryGetValue(from, out list)) 
     { 
      if (list.Contains(to)) 
       return true; 
     } 

     if (to.IsEnum) 
     { 
      return CanCast(from, Enum.GetUnderlyingType(to)); 
     } 
     if (Nullable.GetUnderlyingType(to) != null) 
     { 
      return CanCast(from, Nullable.GetUnderlyingType(to)); 
     } 

     return false; 
    } 

    // https://msdn.microsoft.com/en-us/library/y5b434w4.aspx 
    static Dictionary<Type,List<Type>> ImplicitNumericConversions = new Dictionary<Type, List<Type>>(); 

    static AvailableCastChecker() 
    { 
     ImplicitNumericConversions.Add(typeof(sbyte), new List<Type> {typeof(short), typeof(int), typeof(long), typeof(float), typeof(double), typeof(decimal) }); 
     ImplicitNumericConversions.Add(typeof(byte), new List<Type> { typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) }); 
     ImplicitNumericConversions.Add(typeof(short), new List<Type> { typeof(int), typeof(long), typeof(float), typeof(double), typeof(decimal) }); 
     ImplicitNumericConversions.Add(typeof(ushort), new List<Type> { typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) }); 
     ImplicitNumericConversions.Add(typeof(int), new List<Type> { typeof(long), typeof(float), typeof(double), typeof(decimal) }); 
     ImplicitNumericConversions.Add(typeof(uint), new List<Type> { typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) }); 
     ImplicitNumericConversions.Add(typeof(long), new List<Type> { typeof(float), typeof(double), typeof(decimal) }); 
     ImplicitNumericConversions.Add(typeof(char), new List<Type> { typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) }); 
     ImplicitNumericConversions.Add(typeof(float), new List<Type> { typeof(double) }); 
     ImplicitNumericConversions.Add(typeof(ulong), new List<Type> { typeof(float), typeof(double), typeof(decimal) }); 
    } 

    static bool HasImplicitConversion(Type definedOn, Type baseType, Type targetType) 
    { 
     return definedOn.GetMethods(BindingFlags.Public | BindingFlags.Static) 
      .Where(mi => mi.Name == "op_Implicit" && mi.ReturnType == targetType) 
      .Any(mi => 
      { 
       ParameterInfo pi = mi.GetParameters().FirstOrDefault(); 
       return pi != null && pi.ParameterType == baseType; 
      }); 

    } 
} 
+0

枚舉處理是令人驚訝的,因爲它似乎沒有遵循與其他所有規則相同的規則。有關詳細信息,請參閱http://hastebin.com/rexuzaraju。也許這沒問題,但這是最初的問題中最不起眼的一點。 –

+0

字典中使用的正確類型是HashSet – sam