'T'유형의 값은로 변환 할 수 없습니다


146

이것은 초보자 질문 일 가능성이 있지만 Google은 놀랍게도 답변을 제공하지 않았습니다.

이 인공적인 방법이 있습니다

T HowToCast<T>(T t)
{
    if (typeof(T) == typeof(string))
    {
        T newT1 = "some text";
        T newT2 = (string)t;
    }

    return t;
}

C ++ 배경에서 오는 것은 이것이 효과가 있다고 예상했습니다. 그러나 위의 두 가지 할당 모두에 대해 " 'T'유형을 문자열로 암시 적으로 변환 할 수 없음"및 " 'T'유형을 문자열로 변환 할 수 없음"으로 컴파일되지 않습니다.

개념적으로 잘못된 일을하거나 구문이 잘못되었습니다. 이걸 정리해주세요.

감사합니다!


20
IMO, 제네릭 코드에서 유형을 확인하는 경우 제네릭이 문제의 올바른 해결책이 아닐 수 있습니다.
— Austin Salonen

식은 typeof(T) == typeof(string)컴파일 타임이 아니라 런타임에 해결됩니다. 따라서 블록의 다음 줄은 유효하지 않습니다.
— Steve Guidi

8
(T) Convert.ChangeType (newT1, typeof (T))
— vsapiha

2
@vsapiha, 객체가 IConvertible을 구현 한 경우에만 작동합니다. 그래도 단맛.
— ouflak

답변:


285

그것이 내부 비록 if블록, 컴파일러는 모르는 T것입니다 string.
따라서 캐스팅 할 수 없습니다. (당신이 캐스팅 할 수없는 것과 같은 이유 DateTime에 string)

당신은에 캐스트 할 필요가 object(이 어떤, T위해 할 수있는 캐스트), 거기에에서 string(이후 object에 할 수 있습니다 캐스트 string).
예를 들면 다음과 같습니다.

T newT1 = (T)(object)"some text";
string newT2 = (string)(object)t;

2
작동합니다! 나는 두 번째와 같아야한다고 생각합니다. T newT2 = (T) (object) t; 비록 작전은 아니지만.
— Alex

2
추가 : C ++ 템플릿은 기본적으로 올바른 값을 대체하여 컴파일 타임에 잘라 내기 및 붙여 넣기를 수행합니다. C #에서 실제 제네릭 템플릿 ( "인스턴스화"아님)은 컴파일 후에 존재하므로 지정된 유형 범위에 걸쳐 제네릭이어야합니다.

(문자열) (객체) t; 그래도 여기서 아무것도하지 않을 것입니다. (string) (object)
— Doggett

6
왜 "문자열로"캐스트하지 않습니까? UserDefinedValue (사용자 정의 값) 유형의 경우 예를 들어,이 (말 그대로 단지 오류없이 컴파일) 벌금을 컴파일 T:var isBlank = (userDefinedValue is string) && String.IsNullOrWhiteSpace(userDefinedValue as string);
— Triynko

1
이것은 컴파일러 디자이너들의 실수처럼 느껴집니다. 모든 T가 객체로 신속하게 캐스트 될 수 있고 모든 객체가 문자열로 신속하게 캐스트 될 수 있다면, T가 명시 적으로 스트링으로 캐스트 될 수있는 전이 규칙이 존재해야한다. 캐스트를 잘못 수행하면 runime 오류가 발생합니다.
— P.Brian.Mackey

10

두 줄 모두 같은 문제가 있습니다

T newT1 = "some text";
T newT2 = (string)t;

컴파일러는 T가 문자열임을 알지 못하므로이를 할당하는 방법을 알 수 없습니다. 그러나 당신이 확인했기 때문에 당신은 그것을 강제로 할 수 있습니다.

T newT1 = "some text" as T;
T newT2 = t; 

이미 문자열이기 때문에 t를 캐스팅 할 필요가 없으며 제약 조건을 추가해야합니다.

where T : class

2
잘못된. 컴파일되지 않습니다. 내 대답을 참조하십시오.
— SLaks

2
컴파일이 잘됩니다 (내가 게시 한 후 몇 초 후에 추가 된 위치가 있습니다). 아차 나노 캐스트 변경하는 것을 잊었다
— 도겟

2

OP 가이 질문에 일반 파서에서 게시 한 유사한 코드를 알고 있습니다. 성능 측면 Unsafe.As<TFrom, TResult>(ref TFrom source)에서 System.Runtime.CompilerServices.Unsafe NuGet 패키지에있는를 사용해야 합니다. 이 시나리오에서는 값 유형에 대한 권투를 피합니다. 또한 Unsafe.AsJIT에서 생성 한 머신 코드가 두 번 캐스팅하는 것 ()을 사용하는 것보다 적은 기계 코드를 얻는다고 생각 (TResult) (object) actualString하지만 확인하지 않았습니다.

public TResult ParseSomething<TResult>(ParseContext context)
{
    if (typeof(TResult) == typeof(string))
    {
        var token = context.ParseNextToken();
        string parsedString = token.ParseToDotnetString();
        return Unsafe.As<string, TResult>(ref parsedString);
    }
    else if (typeof(TResult) == typeof(int))
    {
        var token = context.ParseNextToken();
        int parsedInt32 = token.ParseToDotnetInt32();
        // This will not box which might be critical to performance
        return Unsafe.As<int, TResult>(ref parsedInt32); 
    }
    // other cases omitted for brevity's sake
}

Unsafe.As 공식 CoreFX 저장소에서 볼 수 있듯이 효율적인 기계 코드 명령어가있는 JIT로 대체됩니다.

안전하지 않은 소스 코드


1

명시 적 유형을 확인하는 경우 왜 해당 변수를로 선언 T합니까?

T HowToCast<T>(T t)
{
    if (typeof(T) == typeof(string))
    {
        var newT1 = "some text";
        var newT2 = t;  //this builds but I'm not sure what it does under the hood.
        var newT3 = t.ToString();  //for sure the string you want.
    }

    return t;
}

6
두 번째 줄은 유형의 변수를 만듭니다 T.
— SLaks

유형을 확인하는 이유는 무엇입니까? object값 을 저장 하는 파생 유형과 함께 값 을 저장하는 기본 필드 유형이 있다고 가정하십시오 string. 이러한 필드에도 "DefaultIfNotProvided"값이 있으므로 사용자 제공 값 (객체 또는 문자열 또는 숫자 프리미티브 일 수 있음)이와 같은지 확인해야합니다 default(T). 빈 / 공백 문자열이 default (T)와 동일하게 처리되는 특수한 경우로 문자열을 처리 할 수 ​​있으므로 여부를 확인하는 것이 T userValue; var isBlank = (userValue is string) && String.IsNullOrWhitespace(userValue as string);좋습니다.
— Triynko

0

클래스와 메서드 모두에 대한 일반적인 선언이있는 경우에도이 오류가 발생합니다. 예를 들어 아래 코드는이 컴파일 오류를 나타냅니다.

public class Foo <T> {

    T var;

    public <T> void doSomething(Class <T> cls) throws InstantiationException, IllegalAccessException {
        this.var = cls.newInstance();
    }

}

이 코드는 컴파일합니다 (메소드 선언에서 T가 제거됨).

public class Foo <T> {

    T var;

    public void doSomething(Class <T> cls) throws InstantiationException, IllegalAccessException {
        this.var = cls.newInstance();
    }

}

-5

이 줄을 바꾸십시오 :

if (typeof(T) == typeof(string))

이 줄의 경우 :

if (t.GetType() == typeof(string))

1
그들은 동일합니다
— bigworld12

언어 키워드를 사용하는 것과 클래스 라이브러리 API를 사용하는 것만 같습니다.
— Abdulhameed
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.