只能采用某些类型的泛型类

Posted

技术标签:

【中文标题】只能采用某些类型的泛型类【英文标题】:A generic class which can take only certain types 【发布时间】:2018-06-27 18:29:21 【问题描述】:

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

public class A<T> where T: int, double

    public T propertyget;set;

例如:

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

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

但是,这不起作用。

我该怎么做?

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

【问题讨论】:

你不能 - 你将如何在 A 中使用 property 做任何事情? 【参考方案1】:

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");
        
    

【讨论】:

我们可以使用显式构造函数来实现吗? 您可以在 typeof(T) 的构造函数中进行检查,但这将是运行时检查而不是编译时间。 对泛型使用 runtimechecks 对我来说是错误的。不投反对票,只在这里陈述我的意见。 我会将检查放入静态构造函数 static A() if … 【参考方案2】:

您可以编写自己的 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

然后你可以编写你的泛型类:

public class A<T> where T : IMyNumber

    public T property;

你可以使用它:

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

【讨论】:

Integer 类型也可以是struct。在这种情况下,我会从属性中删除 set; 访问器(您仍然可以在构造函数中将其分配给底层的 readonly 字段)。【参考方案3】:

您可以将private 构造函数添加到A&lt;T&gt; 类并声明两个相应的类:A_intA_double 在里面它有可能从A&lt;T&gt; 继承它们 - 它们变成“友好”A&lt;T&gt;。但是对于在该范围之外声明的类(Test 类),由于private 构造函数,它不可能以及直接创建,我们必须调用它,但不能。因此,实际上,您将只有两个可用的 A&lt;T&gt; 变体,并附有编译时间不允许使用的通知:

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&lt;&gt; 之外没有人继承自A&lt;&gt;,只需确保所有非静态构造函数都是私有的。您没有在上面编写任何构造函数,因此您会得到一个自动“默认”构造函数,即protected。为避免这种情况,只需在类中添加A() ,即私有实例构造函数。【参考方案4】:

您可以通过使用带有静态方法的工作区并将构造函数设置为内部来实现这一点。 但是,这仅在 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>();

【讨论】:

【参考方案5】:

根据我对 C# 泛型的理解,您可以使用几种“模式”来设置泛型:

未指定泛型参数是什么,即对每种类型、基元等开放。 通过指定过滤器,例如where T : classwhere T : BaseClass 甚至where T : ISomeInterface,您可以在其中指定传入的类型严格来说必须是类、继承自BaseClass 的类或分别实现ISomeInterface 的类型.

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

public class MyGenericClass<T> 

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

希望这会有所帮助。

【讨论】:

typeof(T) is int 是非法的。 System.Type 类型的类型对象永远不可能是int(继承object -> System.ValueType -> int,而后者是密封的,因为它是值类型)。你可以问typeof(T) != typeof(int)是否代替。 正确,我在浏览器中输入了这个,所以我缺乏 VS 的纠正能力。我将更新上面的代码以反映这一点【参考方案6】:

如果您的唯一目标是防止 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; 

【讨论】:

以上是关于只能采用某些类型的泛型类的主要内容,如果未能解决你的问题,请参考以下文章

Scala入门系列(十三):类型参数

Java泛型知识总结篇

java泛型——泛型类泛型方法泛型接口

JAVA中的泛型类是啥东西?

Java的泛型---(英雄联盟集合嵌套案例)

自定义泛型结构:泛型类泛型接口泛型方法