Type 변수를 사용하여 변수 캐스팅


281

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


12
엄격하게 주제가 아니지만 "캐스트"가 무엇인지에 대해 충분히 모호한 것 같습니다. 캐스트 연산자의 목적과 의미가 무엇인지 정확하게 이해하는 것이 좋습니다. 좋은 시작입니다 : blogs.msdn.com/ericlippert/archive/2009/03/19/…
Eric Lippert

2
나는 무언가를 생각해 냈다고 생각했다. Type변수 가 있으면 리플렉션을 사용하여 해당 유형의 인스턴스를 만들 수 있습니다. 그런 다음 일반 메소드를 사용하여 해당 유형의 매개 변수에서 유추하여 원하는 유형을 리턴 할 수 있습니다. 불행히도 형식의 인스턴스를 만드는 모든 리플렉션 메서드의 반환 유형은 object이므로 일반 CastByExample메서드도 사용 object됩니다. 따라서 실제로이를 수행 할 수있는 방법이 없으며, 존재하는 경우에도 새로 캐스트 된 오브젝트로 무엇을 하시겠습니까? 유형을 모르기 때문에 메소드 나 다른 것을 사용할 수 없습니다.
Kyle Delaney 2012

@KyleDelaney 감사합니다, 나는 완전히 동의합니다! 내 대답에 설명하려고 노력으로, 정말 캐스트에 그 유용하지 뭔가 A와 다른 것은 당신이 실제로 사용하고 있는지 유형을 정의하는 어떤 점에서 않고. 유형의 요점은 컴파일러 시간 유형 검사입니다. 객체를 호출해야하는 경우 object또는 을 사용할 수 있습니다 dynamic. 외부 모듈을 동적으로로드하려는 경우 클래스가 공통 인터페이스를 공유하고 해당 오브젝트를 캐스트 할 수 있습니다. 타사 코드를 제어하지 않으면 작은 래퍼를 만들고 해당 인터페이스를 구현하십시오.
Zyphrax

답변:


203

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

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변수 에 저장하십시오 .
  • 제네릭 은 정확한 유형을 알 필요없이 다양한 유형에 적용되는 재사용 가능한 코드를 만드는 좋은 방법입니다.
  • 붙어 있다면 다른 접근법이나 코드 리 팩터를 고려하십시오. 코드가 정말로 역동적이어야합니까? 모든 유형을 설명해야합니까?

145
이것이 어떻게 OP를 돕는 지 모르겠습니다. 그녀는 유형 변수가 T있습니다.
nawfal

12
@nawfal, 기본적으로 라인 Convert.ChangeType(input, typeof(T));이 솔루션을 제공합니다. typeof(T)기존 유형 변수로 쉽게 바꿀 수 있습니다 . 더 나은 해결책은 가능한 경우 동적 유형을 함께 방지하는 것입니다.
Zyphrax

59
@Zyphrax, 아직 T사용할 수없는 캐스트가 필요 합니다.
nawfal

4
결과 객체는 실제로 유형 T이지만 여전히 object참조 로만 사용 한다는 것을 알고 있습니다 . 흠, OP에는 Type변수 만 있고 다른 정보는 없다는 전제에서 흥미로운 질문을 발견했습니다 . 비록 메소드 서명이 Convert(object source, Type destination):) 그럼에도 불구하고 나는 요점을 얻는다
nawfal

10
이 질문에 대한 해결책은 무엇입니까? 나는 같은 문제가 있고 일반적인 <T>가 없습니다. 유형 변수 만 있습니다.
누리 타스 데 미르

114

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

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

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


19
이것이 정답입니다. 동적 키워드가 없으면 typeof (changedObj)는 "object"입니다. 동적 키워드를 사용하면 완벽하게 작동하며 typeof (changedObject)는 typeVar와 동일한 유형을 올바르게 반영합니다. 또한 유형을 모르면 할 수없는 (T) 캐스트 할 필요가 없습니다.
rushinge

5
이 솔루션을 사용하는 동안 "개체가 IConvertible을 구현해야합니다"예외가 있습니다. 어떤 도움?
Nuri Tasdemir

@NuriTasdemir 말하기는 어렵지만 IConvertible 없이는 전환이 불가능하다고 생각합니다. 전환과 관련된 유형은 무엇입니까?
maulik13 2016 년

이것이 작동하는 동안 역학을 사용하면 성능이 저하됩니다. 다른 런타임 (동적을 위해 설계된 것)으로 작업하지 않는 한 사용하지 않는 것이 좋습니다.
Bolo

19

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


2
요구 사항using System.Linq.Expressions;
Aaron D

4
나에게 이것은 Zyphrax의 대답과 같은 문제로 고통받습니다. 리턴 된 오브젝트가 여전히 "오브젝트"유형이므로 메소드를 호출 할 수 없습니다. 그의 방법 (아래의 "a") 또는 귀하의 방법 (아래의 "b")을 사용하든간에 (t) 캐스트에서 동일한 오류가 발생합니다. " 't'는 변수이지만 유형처럼 사용됩니다.Type t = typeof(MyGeneric<>).MakeGenericType(obj.OutputType); var a = (t)Convert.ChangeType(obj, t); var b = (t)Caster.Cast(t, obj);
muusbolla

@muusbolla Zyphrax의 원래 답변은가 아닌 제네릭과 형식 변수를 사용합니다 Type. Type 객체 만 있으면 일반 캐스팅 구문을 사용하여 캐스팅 할 수 없습니다. 런타임이 아닌 컴파일 타임에 객체를 유형 T로 사용하려면 객체를 유형 변수 또는 실제 유형 이름 만 사용하여 캐스팅해야합니다. Zaphrax의 답변을 사용하여 전자를 수행 할 수 있습니다.
Ashley

8

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

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

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

1
내 질문과 관련하여 더 명확하게 설명해 주시겠습니까?
theringostarrs 2016 년

내가 설명하려는 것은 그 후에 무엇을 할 수 있을까요? C # 컴파일러는 객체 유용한 일을 수행 할 수 있도록 정적 입력을 필요로 당신은 많은 일을 할 수
메흐 다드 Afshari

네가 옳아. 'object'유형으로 메소드에 전송되는 두 변수의 예상 유형을 알고 있습니다. 변수에 저장된 예상 유형으로 캐스트하고 컬렉션에 추가하고 싶습니다. 유형별로 분기하고 일반 캐스트 및 캐치 오류를 시도하는 것이 훨씬 쉽습니다.
theringostarrs 2016 년

4
당신의 대답은 좋지만 단지 까다 롭기 때문에 캐스트는 변수에 영향을 미치지 않습니다 . 캐스팅 법적 결코 변수를 다른 유형의 변수에; 변수 유형은 C #에서 변하지 않습니다. 변수에 저장된 을 다른 유형으로 만 캐스트 할 수 있습니다 .
Eric Lippert 2016 년

C # 4.0의 동적 입력 방식이이 답변을 변경합니까?
Daniel T.

6

어떻게 그렇게 할 수 있습니까? 캐스트 후 오브젝트를 저장할 수있는 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?

3
T 클래스의 반환 값으로 메서드를 정의하는 일반 클래스를 사용하는 경우 그렇게해야 할 수 있습니다. 예를 들어 문자열을 T의 인스턴스로 구문 분석하고 반환합니다.
Oliver Friedrich

7
다행스럽게도 정답은 아닙니다. maulik13의 답변을 참조하십시오.
rushinge

3
어디 하늘의 이름으로 당신은 찾을 수 있습니까 CastTo에 대한 방법을 Object?
ProfK

3

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

1

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

Newtonsoft.Json nuget 패키지 사용 ...

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

1

해, 문제는 T가 없다는 것입니다.

Type 변수 만 있습니다.

당신이 할 수 있다면 MS에 힌트

TryCast<typeof(MyClass)>

모든 문제를 해결할 수 있다면


0

나는 당신이 왜 코멘트를 남기기 위해 최대 50 명 평판을 필요로하는지 이해하지 못할 것입니다. 그러나 @Curt 답변은 내가 찾고있는 것과 정확히 다른 누군가라고 말합니다.

내 예제에는 json 패치 문서의 값을 업데이트하는 데 사용했던 ActionFilterAttribute가 있습니다. 패치 문서의 T 모델이 무엇인지 일반 JsonPatchDocument로 직렬화 및 역 직렬화하고 수정 한 다음 유형을 가지고 있기 때문에 유형을 다시 직렬화 및 역 직렬화해야했습니다.

Type originalType = //someType that gets passed in to my constructor.

var objectAsString = JsonConvert.SerializeObject(myObjectWithAGenericType);
var plainPatchDocument = JsonConvert.DeserializeObject<JsonPatchDocument>(objectAsString);

var plainPatchDocumentAsString= JsonConvert.SerializeObject(plainPatchDocument);
var modifiedObjectWithGenericType = JsonConvert.DeserializeObject(plainPatchDocumentAsString, originalType );

-1
public bool TryCast<T>(ref T t, object o)
{
    if (
        o == null
        || !typeof(T).IsAssignableFrom(o.GetType())
        )
        return false;
    t = (T)o;
    return true;
}

2
이 답변이 다른 답변과 어떻게 다른지,이 솔루션이 적합한 곳을 알려 주시겠습니까?
Klaus Gütter

-2

심지어 더 깨끗한 :

    public static bool TryCast<T>(ref T t, object o)
    {
        if (!(o is T))
        {
            return false;
        }

        t = (T)o;
        return true;
    }

-2

대상 유형을 몰라도 런타임에 객체를 캐스트해야하는 경우 리플렉션을 사용하여 동적 변환기를 만들 수 있습니다.

이것은 캐싱 생성 방법이없는 단순화 된 버전입니다.

    public static class Tool
    {
            public static object CastTo<T>(object value) where T : class
            {
                return value as T;
            }

            private static readonly MethodInfo CastToInfo = typeof (Tool).GetMethod("CastTo");

            public static object DynamicCast(object source, Type targetType)
            {
                return CastToInfo.MakeGenericMethod(new[] { targetType }).Invoke(null, new[] { source });
            }
    }

그런 다음 호출 할 수 있습니다.

    var r = Tool.DynamicCast(myinstance, typeof (MyClass));
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.