[C#] Type 변수를 사용하여 변수 캐스팅

C #에서 객체 유형의 변수를 T 유형의 변수로 캐스트 할 수 있습니까?



답변

캐스트 및 변환의 예는 다음과 같습니다.

using System;

public T CastObject<T>(object input) {   
    return (T) input;   
}

public T ConvertObject<T>(object input) {
    return (T) Convert.ChangeType(input, typeof(T));
}

편집하다:

의견의 일부 사람들은이 답변이 질문에 대답하지 않는다고 말합니다. 그러나이 라인 (T) Convert.ChangeType(input, typeof(T))은 솔루션을 제공합니다. 이 Convert.ChangeType메소드는 모든 오브젝트를 두 번째 인수로 제공된 유형으로 변환하려고 시도합니다.

예를 들면 다음과 같습니다.

Type intType = typeof(Int32);
object value1 = 1000.1;

// Variable value2 is now an int with a value of 1000, the compiler 
// knows the exact type, it is safe to use and you will have autocomplete
int value2 = Convert.ChangeType(value1, intType);

// Variable value3 is now an int with a value of 1000, the compiler
// doesn't know the exact type so it will allow you to call any
// property or method on it, but will crash if it doesn't exist
dynamic value3 = Convert.ChangeType(value1, intType);

나는 당신이 캐스팅하려 할 때 매우 가능성이 코드 냄새의 서명 생각 때문에, 제네릭 답을 작성했습니다 a somethinga something else실제 유형을 처리하지 않고. 99.9 %의 시간이 필요하지 않은 적절한 인터페이스. 이해가 가까워 질 수있는 몇 가지 중요한 경우가있을 수 있지만 이러한 경우는 피하는 것이 좋습니다.

편집 2 :

몇 가지 추가 팁 :

  • 코드를 가능한 한 안전하게 입력하십시오. 컴파일러가 유형을 알지 못하면 코드가 올바른지 여부를 확인할 수 없으며 자동 완성과 같은 기능이 작동하지 않습니다. 간단히 말해서 : 컴파일 타임에 유형을 예측할 수 없다면 컴파일러는 어떻게 할 수 있습니까?
  • 작업중인 클래스가 공통 인터페이스구현하는 경우 해당 인터페이스에 값을 캐스트 할 수 있습니다. 그렇지 않으면 자신 만의 인터페이스를 만들고 클래스가 해당 인터페이스를 구현하도록하십시오.
  • 동적으로 가져 오는 외부 라이브러리를 사용하는 경우 공통 인터페이스도 확인하십시오. 그렇지 않으면 인터페이스를 구현하는 작은 랩퍼 클래스 작성을 고려하십시오.
  • 객체를 호출하고 싶지만 유형에 신경 쓰지 않으려면 값을 object또는 dynamic변수 에 저장하십시오 .
  • 제네릭 은 정확한 유형을 알 필요없이 다양한 유형에 적용되는 재사용 가능한 코드를 만드는 좋은 방법입니다.
  • 붙어 있다면 다른 접근법이나 코드 리 팩터를 고려하십시오. 코드가 정말로 역동적이어야합니까? 모든 유형을 설명해야합니까?

답변

다른 답변에는 “동적”유형이 언급되어 있지 않습니다. 따라서 하나 이상의 답변을 추가하기 위해 “동적”유형을 사용하여 변환 된 객체를 정적 유형으로 캐스트하지 않고도 결과 객체를 저장할 수 있습니다.

dynamic changedObj = Convert.ChangeType(obj, typeVar);
changedObj.Method();

“동적”을 사용하면 컴파일러는 정적 유형 검사를 무시하므로주의하지 않으면 런타임 오류가 발생할 수 있습니다.


답변

다음은 객체를 캐스팅하지만 일반 유형 변수가 아닌 System.Type동적으로 캐스팅하는 방법입니다 .

System.Linq.Expressions, type을 사용하여 런타임에 람다 식을 작성하여 Func<object, object>입력을 개봉하고 원하는 유형 변환을 수행 한 다음 결과를 박스로 표시합니다. 캐스팅 된 모든 유형뿐만 아니라 언 캐스트 단계 때문에 캐스팅 된 유형에도 새로운 유형이 필요합니다. 이러한 표현식을 작성하는 것은 반영, 컴파일 및 동적 메소드 빌드로 인해 시간이 많이 소요됩니다. 운 좋게 생성 된 표현식은 오버 헤드없이 반복적으로 호출 될 수 있으므로 각 표현식을 캐시합니다.

private static Func<object, object> MakeCastDelegate(Type from, Type to)
{
    var p = Expression.Parameter(typeof(object)); //do not inline
    return Expression.Lambda<Func<object, object>>(
        Expression.Convert(Expression.ConvertChecked(Expression.Convert(p, from), to), typeof(object)),
        p).Compile();
}

private static readonly Dictionary<Tuple<Type, Type>, Func<object, object>> CastCache
= new Dictionary<Tuple<Type, Type>, Func<object, object>>();

public static Func<object, object> GetCastDelegate(Type from, Type to)
{
    lock (CastCache)
    {
        var key = new Tuple<Type, Type>(from, to);
        Func<object, object> cast_delegate;
        if (!CastCache.TryGetValue(key, out cast_delegate))
        {
            cast_delegate = MakeCastDelegate(from, to);
            CastCache.Add(key, cast_delegate);
        }
        return cast_delegate;
    }
}

public static object Cast(Type t, object o)
{
    return GetCastDelegate(o.GetType(), t).Invoke(o);
}

이것은 마술이 아닙니다. dynamic키워드에서 와 마찬가지로 코드에서는 캐스팅이 발생하지 않으며 객체의 기본 데이터 만 변환됩니다. 컴파일 타임에 우리는 여전히 객체의 유형이 무엇인지 정확하게 파악 하여이 솔루션을 비실용적으로 만듭니다. 나는 이것을 임의의 유형으로 정의 된 변환 연산자를 호출하는 핵으로 썼다. 그러나 아마도 누군가 더 나은 사용 사례를 찾을 수있을 것이다.


답변

단순함을 위해 박싱과 언 박싱을 제쳐두고 상속 계층 구조를 따라 캐스팅하는 것과 관련된 특정 런타임 작업은 없습니다. 그것은 대부분 컴파일 타임 일입니다. 기본적으로 캐스트는 변수 값을 다른 유형으로 처리하도록 컴파일러에 지시합니다.

캐스트 후 무엇을 할 수 있습니까? 타입을 모르기 때문에 어떤 메소드도 호출 할 수 없습니다. 당신이 할 수있는 특별한 일은 없을 것입니다. 특히 컴파일 타임에 가능한 유형을 알고 수동으로 캐스트하고 if명령문을 사용 하여 각 케이스를 개별적으로 처리하는 경우에만 유용 합니다.

if (type == typeof(int)) {
    int x = (int)obj;
    DoSomethingWithInt(x);
} else if (type == typeof(string)) {
    string s = (string)obj;
    DoSomethingWithString(s);
} // ...


답변

어떻게 그렇게 할 수 있습니까? 캐스트 후 오브젝트를 저장할 수있는 T 유형의 변수 또는 필드가 필요하지만 런타임시 T 만 알고있는 경우 어떻게 이러한 변수 또는 필드를 가질 수 있습니까? 따라서 불가능합니다.

Type type = GetSomeType();
Object @object = GetSomeObject();

??? xyz = @object.CastTo(type); // How would you declare the variable?

xyz.??? // What methods, properties, or fields are valid here?


답변

Enum 유형으로 캐스팅 할 때 :

private static Enum GetEnum(Type type, int value)
    {
        if (type.IsEnum)
            if (Enum.IsDefined(type, value))
            {
                return (Enum)Enum.ToObject(type, value);
            }

        return null;
    }

그리고 당신은 그것을 다음과 같이 부를 것입니다 :

var enumValue = GetEnum(typeof(YourEnum), foo);

이것은 여러 enum 유형의 Description 속성 값을 int 값으로 얻는 경우에 필수적이었습니다.

public enum YourEnum
{
    [Description("Desc1")]
    Val1,
    [Description("Desc2")]
    Val2,
    Val3,
}

public static string GetDescriptionFromEnum(Enum value, bool inherit)
    {
        Type type = value.GetType();

        System.Reflection.MemberInfo[] memInfo = type.GetMember(value.ToString());

        if (memInfo.Length > 0)
        {
            object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), inherit);
            if (attrs.Length > 0)
                return ((DescriptionAttribute)attrs[0]).Description;
        }

        return value.ToString();
    }

그리고:

string description = GetDescriptionFromEnum(GetEnum(typeof(YourEnum), foo));
string description2 = GetDescriptionFromEnum(GetEnum(typeof(YourEnum2), foo2));
string description3 = GetDescriptionFromEnum(GetEnum(typeof(YourEnum3), foo3));

또는 (더 나은 접근 방식) 그러한 캐스팅은 다음과 같습니다.

 private static T GetEnum<T>(int v) where T : struct, IConvertible
    {
        if (typeof(T).IsEnum)
            if (Enum.IsDefined(typeof(T), v))
            {
                return (T)Enum.ToObject(typeof(T), v);
            }

        throw new ArgumentException(string.Format("{0} is not a valid value of {1}", v, typeof(T).Name));
    }


답변

Zyphrax의 답변을 사용할 때 “인터페이스는 IConvertible을 구현해야 함”예외를 피할 수없는 것을 발견 한 후 (인터페이스 구현 제외). 나는 약간의 비 전통적인 것을 시도하고 내 상황에서 일했습니다.

Newtonsoft.Json nuget 패키지 사용 …

var castedObject = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(myObject), myType);