タイプ 'T'の値は変換できません


146

これはおそらく初心者の質問ですが、グーグルは意外にも答えを提供しませんでした。

私はこのかなり人工的な方法を持っています

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、ジェネリックコードで型をチェックしている場合、ジェネリックはおそらく問題の正しい解決策ではありません。
オースティンサローネン2010年

typeof(T) == typeof(string)はコンパイル時ではなく実行時に解決されます。したがって、ブロック内の次の行は無効です。
スティーブギディ

8
(T)Convert.ChangeType(newT1、typeof(T))
vsapiha 2012年

2
@vsapiha、オブジェクトがIConvertibleを実装する場合にのみ機能します。もしそうなら甘さ。
ouflak 2017

回答:


285

それがifブロックの内側にあるとしても、コンパイラはそれがでTあることを知りませんstring
したがって、キャストすることはできません。(あなたはキャストすることができないのと同じ理由DateTimestring

にキャストする必要がありますobject(誰でもTキャストできます)。そしてそこからstringobjectにキャストできるためstring)。
例えば:

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

2
これでうまくいきます!私は2番目のようなものもT newT2 =(T)(object)tでなければならないことを推測しています。それはノーオペレーションですが。
Alex

2
追加:C ++テンプレートは、基本的にコンパイル時に正しい値に置き換えられたカットアンドペーストです。C#では、実際の汎用テンプレート(その「インスタンス化」ではない)はコンパイル後に存在するため、指定された型の境界を越えて(しゃれを許して)汎用でなければなりません。

(文字列)(オブジェクト)t; ただし、ここでは何も行わず、その(文字列)(オブジェクト)も
省略

6
「as string」を使用してキャストしないのはなぜですか?たとえば、このコンパイルの罰金(私は文字通りエラーなしでそれをコンパイル)userDefinedValueのタイプがあるTvar 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を割り当てる方法を知る方法がありません。しかし、チェックしたので、それを強制することができます

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

tは既に文字列であるため、キャストする必要はありません。また、制約を追加する必要があります。

where T : class

2
違う。これはコンパイルされません。私の答えを見てください。
10

2
コンパイルは正常に行われます(つまり、投稿後数秒すると、見落とされた可能性があります)。おっとnmはキャストを変更するのを忘れていました
Doggett

2

この質問でOPが一般的なパーサーから投稿した同様のコードを知っています。パフォーマンスの観点からUnsafe.As<TFrom, TResult>(ref TFrom source)は、System.Runtime.CompilerServices.Unsafe NuGetパッケージにあるを使用する必要があります。これらのシナリオでは、値タイプのボックス化を回避します。私もそう思いますUnsafe.As、JITによって生成されるマシンコードは2回キャストする(を使用して(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に置き換えられます。

Unsafe.Asのソースコード


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
2行目は、型の変数を作成しますT
10

タイプをチェックする理由を尋ねますか?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.