كيفية معرفة ما إذا كان النوع A هو قابلة للتحويل ضمنيا لكتابة B

StackOverflow https://stackoverflow.com/questions/2224266

سؤال

نظرا النوع A والنوع B، كيف يمكنني، في وقت التشغيل، حدد ما إذا كان هناك تحويل ضمني من A إلى B؟

إذا لم يكن ذلك منطقيا، فاعتبر الطريقة التالية:

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

وهنا رمز الاتصال الذي أريد عمله:

// 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");
هل كانت مفيدة؟

المحلول

لاحظ أن IsAssignableFrom لا يحل مشكلتك. يجب عليك استخدام الانعكاس مثل ذلك. لاحظ الحاجة الصريحة للتعامل مع الأنواع البدائية؛ هذه القوائم هي لكل §6.1.2 (التحويلات الرقمية الضمنية) للمواصفات.

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

الاستعمال:

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

نصائح أخرى

التحويلات الضمنية التي ستحتاج إليها:

  • هوية
  • SBYTE إلى القصير، int، طويل، تعويم، مزدوج، أو عشري
  • بايت إلى قصيرة، ushort، int، uint، طويل، Ulong، تعويم، مزدوج، أو عشري
  • قصيرة إلى int، طويل، تعويم، مزدوج، أو عشري
  • ushort إلى int، uint، طويل، ulong، تعويم، مزدوج، أو عشري
  • int الطويل، تعويم، مزدوج، أو عشري
  • uint لفترة طويلة، أولونغ، تعويم، مزدوج، أو عشري
  • طويل للطفو، مزدوج، أو عشري
  • Ulong لتطفو، مزدوج، أو عشري
  • char to ushort، int، uint، طويل، Ulong، تعويم، مزدوج، أو عشري
  • تعويم لمضاعفة
  • تحويل نوع nullable.
  • نوع المرجع إلى الكائن
  • فئة مشتقة إلى الطبقة الأساسية
  • الطبقة إلى الواجهة المنفذة
  • واجهة إلى واجهة الأساسية
  • صفيف للمصفيف عندما يكون للمصفوفات نفس العدد من الأبعاد، هناك تحويل ضمني من نوع عنصر المصدر إلى نوع عنصر الوجهة ونوع عنصر المصدر ونوع عنصر الوجهة أنواع مرجعية
  • نوع مجموعة إلى system.array
  • مجموعة صفيف إلى Ilist <> واجهاتها الأساسية
  • مندوب نوع إلى system.delegate
  • تحويل الملاكمة
  • نوع Enum إلى system.enum
  • تحويل المستخدم المعرفة (OP_IMPLEIVE)

أفترض أنك تبحث عن الأخير. ستحتاج إلى كتابة شيء يشبه مترجم لتغطية جميعهم. ملحوظ هو أن system.linq.expressions.expression لم يحاول هذا الفذ.

الإجابة المقبولة على هذا السؤال تعالج العديد من الحالات، ولكن ليس كل شيء. على سبيل المثال، هنا مجرد عدد قليل من المقصود / التحويلات الصحيحة التي لا يتم التعامل معها بشكل صحيح:

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

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

أدناه، لقد نشرت نسخة بديلة من هذه الوظيفة التي يجيب على وجه التحديد على سؤال المصابيح الضمنية والتحويلات الضمنية. لمزيد من التفاصيل، جناح اختبار اعتدت للتحقق منه، والإصدار المصبوب الصريح، يرجى مراجعة مشاركتي حول هذا الموضوع.

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));
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top