Frage

Bei Typ A und Typ B, wie kann ich zur Laufzeit festzustellen, ob es eine implizite Konvertierung von A nach B?

Wenn das nicht Sinn machen, sollten Sie die folgende Methode:

public PropertyInfo GetCompatibleProperty<T>(object instance, string propertyName)
{
   var property = instance.GetType().GetProperty(propertyName);

   bool isCompatibleProperty = !property.PropertyType.IsAssignableFrom(typeof(T));
   if (!isCompatibleProperty) throw new Exception("OH NOES!!!");

   return property;   
}

Und hier ist der anrufende Code, den ich arbeiten will:

// Since string.Length is an int property, and ints are convertible
// to double, this should work, but it doesn't. :-(
var property = GetCompatibleProperty<double>("someStringHere", "Length");
War es hilfreich?

Lösung

Beachten Sie, dass IsAssignableFrom Ihr Problem nicht lösen. Sie haben Reflexion zu verwenden, wie so. Beachten Sie die explizite Notwendigkeit, die primitiven Typen zu handhaben; Diese Listen sind pro §6.1.2 (implizite numerische Konvertierungen) der Spezifikation.

static class TypeExtensions { 
    static Dictionary<Type, List<Type>> dict = new Dictionary<Type, List<Type>>() {
        { typeof(decimal), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char) } },
        { typeof(double), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float) } },
        { typeof(float), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float) } },
        { typeof(ulong), new List<Type> { typeof(byte), typeof(ushort), typeof(uint), typeof(char) } },
        { typeof(long), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(char) } },
        { typeof(uint), new List<Type> { typeof(byte), typeof(ushort), typeof(char) } },
        { typeof(int), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(char) } },
        { typeof(ushort), new List<Type> { typeof(byte), typeof(char) } },
        { typeof(short), new List<Type> { typeof(byte) } }
    };
    public static bool IsCastableTo(this Type from, Type to) { 
        if (to.IsAssignableFrom(from)) { 
            return true; 
        }
        if (dict.ContainsKey(to) && dict[to].Contains(from)) {
            return true;
        }
        bool castable = from.GetMethods(BindingFlags.Public | BindingFlags.Static) 
                        .Any( 
                            m => m.ReturnType == to &&  
                            (m.Name == "op_Implicit" ||  
                            m.Name == "op_Explicit")
                        ); 
        return castable; 
    } 
} 

Verbrauch:

bool b = typeof(A).IsCastableTo(typeof(B));

Andere Tipps

Implizite Konvertierungen Sie berücksichtigen müssen:

  • Identität
  • sbyte zu kurz, int, long, float, double oder dezimal
  • Byte zu kurz, ushort, int, uint, long, ulong, float, double oder dezimal
  • kurz auf int, long, float, double oder dezimal
  • ushort in int, uint, long, ulong, float, double oder dezimal
  • int in long, float, double oder dezimal
  • uint zu lang, ulong, float, double oder dezimal
  • lange schweben, Doppel-, oder dezimal
  • ulong schweben, Doppel-, oder dezimal
  • char ushort, int, uint, long, ulong, float, double oder dezimal
  • Schwimmer verdoppeln
  • Nullable Typ Umwandlung
  • Referenztyp Objekt
  • Abgeleitete Klasse Basisklasse
  • Klasse implementierte Schnittstelle
  • Schnittstelle zur Basis-Schnittstelle
  • Array zu Array, wenn Arrays die gleiche Anzahl von Dimensionen hat, gibt es eine implizite Konvertierung von dem Quellenelement Typ vom Typ Zielelement und der Source-Elementtyp und der Zielelementtyp ist Referenztypen
  • Array Typ System.Array
  • Array Typ IList <> und seine Basis-Schnittstellen
  • Delegierter Typ System.Delegate
  • Boxen Umwandlung
  • Enum Typ System.Enum
  • Benutzerdefinierte Konvertierung (op_implicit)

Ich nehme an, Sie für letztere suchen. Sie werden so etwas wie ein Compiler zu decken alle von ihnen schreiben müssen. Bemerkenswert ist, dass System.Linq.Expressions.Expression nicht diese Leistung versuchen.

Die akzeptierte Antwort auf diese Frage behandelt viele Fälle, aber nicht alle. Zum Beispiel sind hier nur wenige gültigen Würfe / Umwandlungen, die nicht korrekt behandelt werden:

// explicit
var a = (byte)2;
var b = (decimal?)2M;

// implicit
double? c = (byte)2;
decimal? d = 4L;

Im Folgenden habe ich eine alternative Version dieser Funktion geschrieben, die spezifisch die Frage impliziten Abgüsse und Conversions beantwortet. Weitere Einzelheiten ich die Testsuite verwendet, um es zu überprüfen, und die EINDEUTIG Gussversion, lesen Sie bitte mein Beitrag zum Thema .

public static bool IsImplicitlyCastableTo(this Type from, Type to)
{
    // from http://www.codeducky.org/10-utilities-c-developers-should-know-part-one/ 
    Throw.IfNull(from, "from");
    Throw.IfNull(to, "to");

    // not strictly necessary, but speeds things up
    if (to.IsAssignableFrom(from))
    {
        return true;
    }

    try
    {
        // overload of GetMethod() from http://www.codeducky.org/10-utilities-c-developers-should-know-part-two/ 
        // that takes Expression<Action>
        ReflectionHelpers.GetMethod(() => AttemptImplicitCast<object, object>())
            .GetGenericMethodDefinition()
            .MakeGenericMethod(from, to)
            .Invoke(null, new object[0]);
        return true;
    }
    catch (TargetInvocationException ex)
    {
        return = !(
            ex.InnerException is RuntimeBinderException
            // if the code runs in an environment where this message is localized, we could attempt a known failure first and base the regex on it's message
            && Regex.IsMatch(ex.InnerException.Message, @"^The best overloaded method match for 'System.Collections.Generic.List<.*>.Add(.*)' has some invalid arguments$")
        );
    }
}

private static void AttemptImplicitCast<TFrom, TTo>()
{
    // based on the IL produced by:
    // dynamic list = new List<TTo>();
    // list.Add(default(TFrom));
    // We can't use the above code because it will mimic a cast in a generic method
    // which doesn't have the same semantics as a cast in a non-generic method

    var list = new List<TTo>(capacity: 1);
    var binder = Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(
        flags: CSharpBinderFlags.ResultDiscarded, 
        name: "Add", 
        typeArguments: null, 
        context: typeof(TypeHelpers), // the current type
        argumentInfo: new[] 
        { 
            CSharpArgumentInfo.Create(flags: CSharpArgumentInfoFlags.None, name: null), 
            CSharpArgumentInfo.Create(
                flags: CSharpArgumentInfoFlags.UseCompileTimeType, 
                name: null
            ),
        }
    );
    var callSite = CallSite<Action<CallSite, object, TFrom>>.Create(binder);
    callSite.Target.Invoke(callSite, list, default(TFrom));
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top