泛型 class 只能采用特定类型

A generic class which can take only certain types

假设,我想创建一个泛型 class,它只能将 intdouble 作为类型。

public class A<T> where T: int, double
{
    public T property{get;set;}
}

例如:

A<int> i = new A<int>();
i.property = 10;

A<double> d = new A<double>();
d.property = 0.01;

但是,这不起作用。

我该怎么做?

有没有其他方法可以满足我的特定要求?

C# 中不存在此类约束。但是对于值类型,您可以使用 struct 作为通用约束。它只允许不可为空的值类型。

public class A<T> where T : struct
{
    public T property;
}

您可以在构造函数中添加运行时类型检查:

public class A<T> where T : struct
{
    public T property;
    public A()
    {
        if(typeof(T) != typeof(int) || typeof(T) != typeof(double))
        {
            throw new InvalidConstraintException("Only int or double is supported");
        }
    }
}

您可以编写自己的 Wrapper 并为它们使用基本接口。例如:

public class Integer : IMyNumber
{
    public int Value { get; set; }


    public Integer() { }
    public Integer( int value ) { Value = value; }


    // Custom cast from "int":
    public static implicit operator Integer( Int32 x ) { return new Integer( x ); }

    // Custom cast to "int":
    public static implicit operator Int32( Integer x ) { return x.Value; }


    public override string ToString()
    {
        return string.Format( "Integer({0})", Value );
    }
}


public interface IMyNumber
{
    // nothing needed
}

然后你可以写你的泛型class:

public class A<T> where T : IMyNumber
{
    public T property;
}

你可以使用它:

A<Integer> i = new A<Integer>();
i.property.Value = 10;

你可以在A<T>class中添加private构造函数并声明两个对应的classes:A_intA_doubleinside 它有可能从 A<T> 继承它们 - 它们变成 "friendly"A<T>。但是对于在该范围之外声明的 classes(Test class),由于我们必须调用 private 构造函数,它不可能像直接创建一样,但不能。因此,实际上,您将只有两个可用的 A<T> 变体,并附有 编译时间 不允许使用的通知:

public class A<T> where T : struct
{
    //constructor surely can have arguments
    private A()
    {
    }

    public T property { get; set; }
    //and other common stuff

    //each class declaration below we can treat like "where" constraint
    public class A_int : A<int> { }
    public class A_double : A<double> { }
}

//compile time error:
//'A<bool>.A()' is inaccessible due to its protected level
public class Test : A<bool>
{
}

用法:

using static NameSpaceName.A<int>;
//you should not care about <int> - it is only needed for compiler 
//and won't have any influence

var intVar = new A_int();
var doubleVar = new A_double(); 

//compile time error:
//'A<decimal>.A()' is inaccessible due to its protected level
var decimalVar = new A<decimal>(); 

您可以通过使用带有静态方法的变通方法并将构造函数设为内部来实现此目的。 但是,这仅在 A<> 位于不同的库中时才有效。如果一切都在同一个库中,则此解决方案将不起作用。

public class Program
{
    public static void Main()
    {
        var a = A.CreateDecimal();
        a.property = 7;
    }
}

public class A<T>
{
    public T property;
    internal A()
    {
    }
}

public static class A
{
    public static A<decimal> CreateDecimal() => new A<decimal>();
    public static A<int> CreateInt() => new A<int>();
}

根据我对 C# 泛型的理解,您有几个 "modes" 可以在其中设置您的泛型:

  • 不指定通用参数是什么,即对所有类型、原始类型等开放
  • 通过指定 where T : classwhere T : BaseClass 甚至 where T : ISomeInterface 等过滤器,您在其中指定传入的类型必须严格为 class, class分别继承自BaseClass或实现了ISomeInterface的类型

据我所知,没有办法限制您可以使用的结构或原语。最好的选择可能是使用您要使用的原始类型创建单独的实现。或者,您可以创建一个构造函数来检查传入的类型是否与允许的类型匹配,如果不匹配则抛出异常。

public class MyGenericClass<T> 
{
    public MyGenericClass()
    {
        if(typeof(T) != typeof(int)) throw new Exception("You have passed an invalid type");
    }
}

希望对您有所帮助。

如果您的唯一目标是防止 A 被用于 intdeouble 以外的任何东西,这将起作用。 XML 评论至少可以告诉潜在的开发者他们是有限的。

/// <summary>
/// T can only be an int or double or will throw an exception on construction.
/// </summary>
/// <typeparam name="T">Must be int or double.</typeparam>
public class A<T>
{
    public A()
    {
        if (!(property is int || property is double))
            throw new Exception("A can only work with int and double");
    }
    public T property  { get; set; }
}