使用 "where" 排除(或包含)类型的类型约束隐式转换

Implicit conversion with type constraints using "where" to exclude(or include) types

(问题在底部)

我有一个 class,我需要像这样使用它:

float [] floatArray=new float[1024];

Foo<float> var1=floatArray; // creates foo, assigns the array to a field

var1.compute();

Foo<float> floatArray2 = new Foo<float>(1024);

Foo<float> var2=floatArray2; // creates foo, copies its internal array 

var2.compute();

然后我想我可以使用两种隐式转换,一种用于数组,一种用于非数组。

我可以设法完成第一个版本:

    public static implicit operator Foo<T> (T[] b)
    {
        if (typeof(T) == typeof(int) ||
            typeof(T) == typeof(uint) ||
            typeof(T) == typeof(byte) ||
            typeof(T) == typeof(char) ||
            typeof(T) == typeof(double) ||
            typeof(T) == typeof(long) ||
            typeof(T) == typeof(float))
        {
            Foo<T> foo = new Foo<T>();
            foo.arr = b;

            return foo;
        }
        else
        {
            throw new NotImplementedException();
        }
    }

但这里面有很多检查,我试图在 class 声明中添加约束,例如:

   public class Foo<T> where T:typeof(float),typeof(int)
   {
      ...
   }

但它使编译器抱怨为 "int is not a valid constraint"。

在第二种情况下,我需要排除数组,我试过这个:

    public static implicit operator Foo<T> (Foo<T>  b)
    {         
            Foo<T> foo = new Foo<T>();
            foo.arr = b.arr;

            return foo;
    }

但这次编译器说 "cant take enclosing type and convert to enclosing type" 并加下划线 operator Foo<T>

但这些确实有效:

    public static implicit operator Foo<T> (Foo<float>  b)
    public static implicit operator Foo<float> (Foo<T>  b)

我不想将 int 类型交叉转换为 float,也不希望将 float 交叉转换为 int。只是 T 到相同的 T(不是所有 T 到所有 T)。

问题:如何将隐式转换限制为仅限原始数字数组,如 float、byte、double、long、int 和 1-2 自定义 classes没有在方法主体中添加太多类型检查(自定义 classes 是通用的,带有 float int 字节...)? (其中关键字对方法 <T> 定义不起作用)


Foo 已经实现了 IList,但 IList 可以是 Bar[] 对吗?所以我不能使用界面来缩小我假设的搜索范围。另外我不希望 =Foo 做一个引用赋值,而是创建一个新的(或者只使用当前的,会更好)并复制它的内部数组,就像一个结构, 但仍然具有 class(inheritance, ...).

的许多优点

C# 的通用约束有些限制,您可以将 T 限制为 classstruct,但不能将其限制为只有一种指定类型,如 intfloatdouble。因此,为了解决这个问题,您要么需要像您一样进行手动 if 检查,要么需要为每种类型添加重载的隐式运算符。虽然它需要更多工作,但我会推荐重载解决方案,因为它更安全。

关于第二个问题,不允许添加从类型到自身相同类型的隐式转换。规范中的相关部分对其进行了详细解释(C# Spec 5.0 10.10.3. Conversion Operators):

For a given source type S and target type T, if S or T are nullable types, let S0 and T0 refer to their underlying types, otherwise S0 and T0 are equal to S and T respectively. A class or struct is permitted to declare a conversion from a source type S to a target type T only if all of the following are true:

  • S0 and T0 are different types.
  • Either S0 or T0 is the class or struct type in which the operator declaration takes place.

  • Neither S0 nor T0 is an interface-type.

  • Excluding user-defined conversions, a conversion does not exist from S to T or from T to S.

For the purposes of these rules, any type parameters associated with S or T are considered to be unique types that have no inheritance relationship with other types, and any constraints on those type parameters are ignored. In the example

class C<T> {...}
class D<T>: C<T>
{
  public static implicit operator C<int>(D<T> value) {...}        // Ok
  public static implicit operator C<string>(D<T> value) {...} // Ok
  public static implicit operator C<T>(D<T> value) {...}      // Error
}

the first two operator declarations are permitted because, for the purposes of §10.9.3, T and int and string respectively are considered unique types with no relationship. However, the third operator is an error because C is the base class of D.

注意:当您需要手动检查泛型参数的类型时,通常表明您的代码存在设计缺陷,因此您应该重新考虑将 Foo 设为泛型。它真的必须是通用的吗?我假设这不是您检查 typeof T 的唯一地方,您可能需要在实际使用 T 数组的地方再次检查类型并手动将项目投射到那个键入以便与他们一起操作。

你可以限制Tstruct这会让你已经很好的编译时支持了。
然后,创建一个私有构造函数并在那里进行所有类型检查,包括数组的赋值。
据我所知,没有办法更改相同类型的引用分配,因此无法满足该要求。

public void GenericImplicitTest()
{
    int[] ints = new int[4];
    char[] chars = new char[5];
    Foo<int> foo = ints;
    Foo<char> foo_s = chars;
    // Foo<float> f = ints;  <-- will not compile

}

class Foo<T> where T: struct 
{
    private readonly T[] arr;

    private Foo(T[] arr)
    {
        if (typeof (T) != typeof (int) && typeof (T) != typeof (uint) && typeof (T) != typeof (byte) && typeof (T) != typeof (char) &&
            typeof (T) != typeof (double) && typeof (T) != typeof (long) && typeof (T) != typeof (float))
            throw new NotSupportedException($"{typeof (T)} is not supported");

        this.arr = arr;
    }

    public static implicit operator Foo<T>(T[] b)
    {
        Foo<T> foo = new Foo<T>(b);
        return foo;
    }
}

如果您不想通过构造函数设置数组,则创建一个私有静态 Create 方法;