'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.
따라서 캐스팅 할 수 없습니다. (당신이 캐스팅 할 수없는 것과 같은 이유 DateTimestring)

당신은에 캐스트 할 필요가 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.