无法将类型'Int'隐式转换为'T'


90

我可以打电话Get<int>(Stat);Get<string>(Name);

但是当编译时我得到:

无法将类型'int'隐式转换为'T'

和相同的事情string

public T Get<T>(Stats type) where T : IConvertible
{
    if (typeof(T) == typeof(int))
    {
        int t = Convert.ToInt16(PlayerStats[type]);
        return t;
    }
    if (typeof(T) == typeof(string))
    {
        string t = PlayerStats[type].ToString();
        return t;
    }
}

6
您可能会认为if块检查了T是否为int,因此在该块内,您知道T为int,并且应该能够将int隐式转换为T。但是编译器并非旨在遵循这种推理,它只是知道通常T不是从int派生的,因此它不允许隐式转换。(如果编译器支持它,那么验证程序就不会支持,因此编译后的程序集将无法验证。)
JGWeissman 2011年

Answers:


132

每当您发现自己在通用类型中打开类型时,几乎肯定会做错事情。泛型应该是泛型的; 它们应该完全独立于类型而完全相同操作。

如果T只能是int或string,那么首先不要以这种方式编写代码。编写两个方法,一个返回一个int,另一个返回一个字符串。


1
汽车实现IConvertible的Get <Car>会导致损坏。当有人看到您有通用方法时,他们会假定他们可以传入实现IConvertible的任何内容。
Tjaart 2012年

10
我只能部分同意@Eric,在这种情况下,我必须解析存储在XML标签中的数组。问题是XML文档遵循的规范(在我的情况下为COLLADA)说这样的数组可以不仅是float,int和bool,还包括一些自定义类型。但是,如果您获得了float [](array-tags以其名称包含存储数据的类型:float_array存储floats),则需要将字符串解析为浮点数,这需要使用一些IFormatProvider。)我显然不能使用“ T.Parse(...)”。因此,在少数情况下,我需要使用这种切换。
rbaleksandar

1
这个答案将使您摆脱困境。我想为制作通用的函数int, int?, bool, bool?, string,但这似乎是不可能的。
杰西2015年

这使得在通用枚举类型上进行切换变得切实可行。
戴维·格雷

1
我不想以此作为答案。但是他是对的。我想检查类型,如果是特定类型,请在其上设置属性。解决方案是创建一个采用强类型参数的方法。
马特·道迪

141

您应该能够只使用Convert.ChangeType()而不是自定义代码:

public T Get<T>(Stats type) where T : IConvertible
{
    return (T) Convert.ChangeType(PlayerStats[type], typeof(T));
}

21
怎么样return (T)(object)PlayerStats[type];
maxp

11
public T Get<T>(Stats type ) where T : IConvertible
{
    if (typeof(T) == typeof(int))
    {
        int t = Convert.ToInt16(PlayerStats[type]);
        return (T)t;
    }
    if (typeof(T) == typeof(string))
    {
        string t = PlayerStats[type].ToString();
        return (T)t;
    }
}

2
return (T) t;因为不需要空检查。
BoltClock

上面的内容不会为我编译。T必须是“ as”要编译的引用类型。
罗伯·施密特

9

ChangeType可能是您最好的选择。我的解决方案与BrokenGlass提供的解决方案类似,但具有一些try catch逻辑。

static void Main(string[] args)
{
    object number = "1";
    bool hasConverted;
    var convertedValue = DoConvert<int>(number, out hasConverted);

    Console.WriteLine(hasConverted);
    Console.WriteLine(convertedValue);
}

public static TConvertType DoConvert<TConvertType>(object convertValue, out bool hasConverted)
{
    hasConverted = false;
    var converted = default(TConvertType);
    try
    {
        converted = (TConvertType) 
            Convert.ChangeType(convertValue, typeof(TConvertType));
        hasConverted = true;
    }
    catch (InvalidCastException)
    {
    }
    catch (ArgumentNullException)
    {
    }
    catch (FormatException)
    {
    }
    catch (OverflowException)
    {
    }

    return converted;
}

我的用例是从通用抽象类派生的具体类。该类被标记为抽象,因为它定义了对基类的通用私有成员进行操作的抽象方法。泛型对其泛型类型使用C#7.3枚举约束。我刚刚成功完成了一个测试,它的运行完全符合我的期望。
David A. Gray,


8

实际上,您可以将其转换为object,然后转换为T

T var = (T)(object)42;

的示例bool

public class Program
{
    public static T Foo<T>()
    {
        if(typeof(T) == typeof(bool)) {
            return (T)(object)true;
        }

        return default(T);
    }

    public static void Main()
    {
        bool boolValue = Foo<bool>(); // == true
        string stringValue = Foo<string>(); // == null
    }
}

有时,这种行为是可取的。例如,当从基类或接口实现或重写泛型方法时,您想要基于T类型添加一些不同的功能。


6

考虑@BrokenGlass逻辑(Convert.ChangeType)不支持GUID类型。

public T Get<T>(Stats type) where T : IConvertible
{
    return (T) Convert.ChangeType(PlayerStats[type], typeof(T));
}

错误:从'System.String'到'System.Guid'的无效转换。

相反,TypeDescriptor.GetConverter通过添加System.ComponentModel名称空间使用以下逻辑。

public T Get<T>(Stats type) where T : IConvertible
{
    (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFromInvariantString(PlayerStats[type])
}

阅读



0

您可以像下面这样简单地进行投射,

public T Get<T>(Stats type) where T : IConvertible
{
  if (typeof(T) == typeof(int))
  {
    int t = Convert.ToInt16(PlayerStats[type]);
    return t as T;
  }
 if (typeof(T) == typeof(string))
 {
    string t = PlayerStats[type].ToString();
    return t as T;
 }
}
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.