2015-08-15 17 views
19

Ho due tipi, T e U, e voglio sapere se un operatore cast implicito è definito da T a U.Come posso determinare se esiste un cast implicito in C#?

Sono a conoscenza dell'esistenza di IsAssignableFrom, e questo non è quello che sto cercando, in quanto non si tratta di cast impliciti.

Un po 'di googling mi ha portato a this solution, ma nelle parole dell'autore questo è brutto codice (si cerca di lanciare in modo implicito e restituisce falso se c'è un'eccezione, true in caso contrario ...)

Sembra test per l'esistenza di un metodo op_Implicit con la firma corretta won't work for primitive types.

Esiste un modo più pulito per determinare l'esistenza di un operatore di trasmissione implicita?

+0

Solo un suggerimento: Guarda [operatori di conversione di tipo implicita] (https://msdn.microsoft.com/en-us/library/z5z9kes2.aspx). Immagino che ci dovrebbe essere un modo per trovare gli operatori impliciti attraverso la riflessione ... – elgonzo

+0

Puoi per favore entrare in maggiori dettagli su quale obiettivo stai cercando di ottenere con il risultato? Ho avuto un problema simile in passato, ho capito che avrebbe dovuto somigliare a quello che CliveDM [linkato] (https://github.com/CYJB/Cyjb/blob/f4424c4f81cacd09e9ce5d202a03b0c121c09ac2/Cyjb/TypeExt.cs) [sotto ] (http://stackoverflow.com/a/32025388/1083771), e ha deciso di chiamare semplicemente [Convert.ChangeType] (https://msdn.microsoft.com/en-us/library/dtb69x08.aspx) e gestire eccezioni. Mi rendo conto che potrebbe non essere una soluzione valida nel tuo caso, ma forse c'è una soluzione alternativa simile. –

risposta

2

Here's una soluzione che ho trovato. Il codice principale indicato come muggito (dopo un po 'semplice traduzione):

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); 
} 

Aggiornamento: Here's l'intero file.

+1

Questo codice sembra piuttosto incompleto. – poke

+1

@poke Ho aggiornato la mia risposta, si spera che possa essere d'aiuto. –

+0

'if (fromType.IsByRef) {fromType = type.GetElementType(); } 'chiama' GetElementType' sulla variabile sbagliata. –

14

Si potrebbe utilizzare la riflessione per trovare il metodo di conversione implicita per il tipo di target:

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; 
     }); 
} 

è possibile utilizzarlo in questo modo:

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)); 

notare che questa verifica solo i una conversione di tipo implicita sul tipo di base (il primo tipo passato). Tecnicamente, la conversione del tipo può anche essere definita sull'altro tipo, quindi potrebbe essere necessario richiamarla di nuovo con i tipi invertiti (o crearli nel metodo). Tuttavia, le conversioni di tipo implicito potrebbero non esistere su entrambi i tipi.

+0

Non sembra funzionare per i tipi primitivi, ad esempio MappingsGetter.HasImplicitConversion (typeof (int), typeof (decimal)) restituisce false mentre esiste una conversione implicita: https://msdn.microsoft.it/it/us/library/y5b434w4.aspx – Brann

+3

@Brann Questi tipi non hanno operatori impliciti; la conversione del tipo viene eseguita direttamente dal CLR. Esse implementano ['IConvertible'] (https://msdn.microsoft.com/en-us/library/system.iconvertible.aspx), quindi puoi verificarlo. – poke

+0

@Brann Per i tipi primitivi dovrete solo scrivere un codice da una tabella. Non sono a conoscenza di alcun meccanismo integrato che ti consenta di farlo a livello di programmazione. – Kyle

3

Ho finito per gestire manualmente lo scenario dei tipi primitivi. Non molto elegante, ma funziona.

Ho anche aggiunto ulteriore logica per gestire i tipi nullable e le enumerazioni.

Ho riutilizzato Poke's code per lo scenario di tipo definito dall'utente.

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

La gestione dell'enum è stata sorprendente, in quanto non sembra seguire le stesse regole di tutto il resto. Vedi http://hastebin.com/rexuzaraju per i dettagli. Forse va bene, ma è minimamente qualcosa che è stato lasciato fuori dalla domanda originale. –

+0

Il tipo corretto da utilizzare nel dizionario è HashSet – sam