类型“ 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,如果您要检查泛型代码中的类型,那么泛型可能不是解决问题的正确方法。
奥斯丁·萨洛宁

该表达式typeof(T) == typeof(string)在运行时而不是编译时解析。因此,该块中的下一行无效。
史蒂夫·吉迪

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

2
@vsapiha,仅当对象实现IConvertible时有效。如果有的话就很甜。
ouflak

Answers:


285

即使它在一个if块中,编译器也不知道Tstring
因此,它不允许您进行投射。(出于同样的原因,您无法转换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#中,实际的通用模板(不是模板的“实例化”)在编译后就存在,因此(对双关语必须)在指定的类型范围内是通用的。

(string)(object)t; 不过,这里什么也没做,不妨将其删除,即(string)(object)
Doggett 2010年

6
为什么不只使用“作为字符串”进行转换?例如,当userDefinedValue的类型为Tvar isBlank = (userDefinedValue is string) && String.IsNullOrWhiteSpace(userDefinedValue as string);
Triynko 2015年

1
编译器设计者觉得这是一个错误。如果所有T都可以明确地转换为对象,而所有对象都可以明确地转换为字符串,则应该存在一个传递规则,即T可以明确地转换为字符串。如果您不正确地执行了强制转换,则应该发生运行错误。
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's

2
编译就很好了(在我发布的几秒钟后加上,可能会错过)。糟糕,nm忘记更改演员表
Doggett,2010年

2

我从通用解析器知道OP在此问题中发布的类似代码。从性能的角度来看,你应该使用Unsafe.As<TFrom, TResult>(ref TFrom source),可在发现System.Runtime.CompilerServices.Unsafe NuGet包。在这些情况下,它避免为值类型装箱。我还认为,这Unsafe.As导致JIT生成的机器代码少于两次(使用(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 可以在JIT中使用有效的机器代码说明来代替,如您在官方CoreFX存储库中所看到的:

Unsafe.As的源代码


1

如果要检查显式类型,为什么要将这些变量声明为Ts?

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 2010年

您问为什么要检查类型?假设您有一个存储object值的基本字段类型,以及一个存储值的派生类型string。假设这些字段也具有“ DefaultIfNotProvided”值,因此您需要检查用户提供的值(可以是对象还是字符串,甚至是数字基元)是否等效于default(T)。字符串可能被视为特殊情况,其中空字符串/空白字符串与default(T)相同,因此您可能要检查是否为T userValue; var isBlank = (userValue is string) && String.IsNullOrWhitespace(userValue as string);
Triynko 2015年

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

两者是相同的...只是使用language关键字与使用类库API。
Abdulhameed 19/09/22
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.