문제

유형 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 to short, int, long, float, double 또는 decimal
  • 바이트에서 짧은, Ushort, int, uint, long, ulong, float, double 또는 decimal
  • int, int, long, float, double 또는 decimal까지
  • int, uint, long, ulong, float, double 또는 decimal로 안내합니다.
  • int to long, float, double 또는 cecimal
  • uint to long, ulong, float, double 또는 decimal
  • 플로트, 이중 또는 소수점이 길다
  • Ulong은 떠 다니는 것, 이중 또는 소수점입니다
  • Ushort, int, uint, long, ulong, float, double 또는 decimal to har to ushort
  • 플로트로 두 배
  • 무효 유형 변환
  • 객체에 대한 참조 유형
  • 기본 클래스에서 파생 된 클래스
  • 클래스에서 구현 된 인터페이스
  • 기본 인터페이스에 대한 인터페이스
  • 배열에서 배열에서 배열이 동일한 수의 치수를 갖는 경우 소스 요소 유형에서 대상 요소 유형으로 암시 적 변환이 있으며 소스 요소 유형 및 대상 요소 유형은 참조 유형입니다.
  • Array Type to System.Array
  • ILIST <> 및 기본 인터페이스에 대한 배열 유형
  • System.Delegate에 대의원 유형
  • 권투 변환
  • Enum type to system.enum
  • 사용자 정의 변환 (op_implicit)

나는 당신이 후자를 찾고 있다고 생각합니다. 컴파일러를 모두 다루기 위해 컴파일러와 유사한 것을 작성해야합니다. 주목할만한 것은 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