【问题标题】:C# casting error on generics泛型上的 C# 转换错误
【发布时间】:2023-03-23 04:27:02
【问题描述】:

我有一个 C# 方法,我试图将 generics 用作:

unsafe private void PerformWindowLevel<T>(int lower, int upper, ref T[] pixels)

在函数的某处我有以下行:

float shift = ... // some value
float scale = ... // some value
float val = ((float)pixels[i] + shift) * scale;

在这里我收到错误Cannot convert type 'T' to 'float'

方法调用如下:

PerformWindowLevel<byte>(10, 100, ref pixels);

所以我将 byte 类型转换为应该是可能的浮点数。 pixels 是一个声明为public byte[] pixels; 并填充了有效值的字节数组。

【问题讨论】:

  • 如果用这个方法为什么要使用泛型?
  • float val = (Convert.ToSingle((object)pixels[i] + shift)) * scale 怎么样?应用泛型约束只限于特定类型,如果您想要更广泛的T 类型使用转换方法(注意类型参数 T 只能转换为对象)。
  • @TetsuyaYamamoto 这抱怨我不能在对象和浮点数上使用 (+) 运算符...
  • @DdarkSideE 该方法也将与 UInt16 等其他类型一起使用。它们始终可以转换为浮点数。
  • 似乎我把 Convert.ToSingle 方法放在了错误的范围内:(Convert.ToSingle((object)pixels[i]) + shift)object 需要转换为 float 并与另一个 float 执行加法。

标签: c# generics casting


【解决方案1】:

问题是T 不知道它在你的调用代码中可能是什么,它只是一个通用类型参数,也可能是string 或任何你(显然)可以不要投到float。你需要一个通用的约束:

unsafe private void PerformWindowLevel<T>(int lower, int upper, ref T[] pixels) where T: float

这仍然不起作用,因为您不能使用结构(float 是)作为通用类型参数。只允许使用接口或类。

但是,这只会允许float 成为有效的通用参数,从而使术语泛型非常 - 好吧 - ungeneric。这就是为什么泛型在您的情况下是完全错误的。当只有一种或两种类型可能时,您应该为该特定类型创建具体方法(或重载)。另一方面,泛型声明每种类型(满足where 指示的泛型约束)都是可能的,而不仅仅是一个。

【讨论】:

  • 不行,因为“用作约束的类型必须是接口、非密封类或类型参数。”
【解决方案2】:

无法将类型 'T' 转换为 'float' 表明编译器根本不知道泛型类型参数 T 当前具有哪种数据类型。任何将T 转换为除object(作为所有对象的基类)之外的其他类型的尝试都将导致InvalidCastException

因此,根据上面解释的泛型类型参数的工作原理,代码可以写成这样:

unsafe private void PerformWindowLevel<T>(int lower, int upper, ref T[] pixels)
{
    float shift = 3.0F; // some value
    float scale = 5.0F; // some value
    float val = (Convert.ToSingle((object)pixels[i]) + shift) * scale;
}

或在执行强制转换之前检查byte 数据类型:

unsafe private void PerformWindowLevel<T>(int lower, int upper, ref T[] pixels)
{
    if (typeof(T) == typeof(byte))
    {
        float shift = 3.0F; // some value
        float scale = 5.0F; // some value
        float val = (Convert.ToSingle((object)pixels[i]) + shift) * scale;
    }
}

请注意,泛型本身应该完全独立于类型,因此如果类型参数T 打算只接受byte,只需使用ref byte[] pixels

NB:关于消息用作约束的类型必须是接口,非密封类或类型参数,可以解释floatSystem.Single)本身只是一个struct,隐含地是sealed。结构或密封类不能用作泛型类型参数约束,但您可以约束T 是一个结构。当前有效的约束应该是这样的:

void GenericMethod<T>() where T: class // reference type constraint

void GenericMethod<T>() where T: struct {} // value type constraint

void GenericMethod<T>() where T: new() // public parameterless constructor constraint

void GenericMethod<T>() where T: IConvertible // interface constraint

void GenericMethod<T>() where T: BaseClass // base class constraint

更多信息:Constraints on Type Parameters

类似问题:

Cannot implicitly convert type 'T' to 'Int'

Value of type 'T' cannot be converted to

【讨论】:

    【解决方案3】:

    您需要编写一个单独的 TypeConverter,因为在编译时编译器不知道 Type T 以及它是否可以转换为 float 。

    可以使用 Convert.ChangeType 方法进行转换

    public class ConvertTypeClass<T1,T2>
        {
            public T1 ConvertMethod(T2 val)
            {
                return (T1) (Convert.ChangeType(val, typeof(T1)));
            }
        } 
    

    【讨论】:

      【解决方案4】:

      这是一个已知问题,因为基本算术类型没有通用接口(例如 INumeric),您可以通过 where T : INumeric 来限制您的方法。

      但是,有一个稍微笨拙的解决方法,它确实可以编译!如果您将代码包含在 typeof 条件中,编译器会很高兴。例如:

      private void PerformWindowLevel<T>(int lower, int upper, ref T[] pixels) 
      {
          if ((typeof(T) == typeof(byte)) || (typeof(T) == typeof(int)))
          {
              float shift = 1;
              float scale = 2;
              float val = ((float)(object)pixels[0] + shift) * scale;
          }  
      }
      

      您需要做的就是在您周围的 if 条件中包含您需要的任意数量的类型!正如我所说的有点笨拙,但它有效!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-12-17
        • 2019-07-14
        • 2022-08-27
        • 2013-05-14
        • 2015-11-17
        • 1970-01-01
        相关资源
        最近更新 更多