C#泛型与常量

时间:2011-01-08 22:25:32

标签: c# c++ security templates generics

是否有与此C ++模板类似的内容?

template <int A>
class B
{
    int f()
    {
        return A;
    }
}

我想使B&lt; 1&gt;,B&lt; 2&gt;等的每个实例(例如元组)成为不同的类型。

5 个答案:

答案 0 :(得分:21)

简短的回答是

它不适合与C ++模板相同的C#泛型工作方式。

.net泛型不是语言功能,它们是运行时功能。运行时知道如何从特殊的通用字节码中实例化泛型,与C ++模板可以描述的相比,这是非常有限的。

将此与C ++模板进行比较,C ++模板基本上使用替换类型实例化类的整个AST。可以将基于AST的实例化添加到运行时,但它肯定比当前的泛型要复杂得多。

如果没有值类型数组(仅存在于不安全的代码中)等功能,使用此类参数的递归模板实例化或模板特化也不会非常有用。

答案 1 :(得分:12)

C#不支持像C ++那样的非类型泛型参数。

C#泛型比C ++模板更简单,功能更少。 MSDN有一个简洁的Differences Between C++ Templates and C# Generics列表。

答案 2 :(得分:12)

此限制的解决方法是定义一个类,该类本身公开您感兴趣的文字值。例如:

public interface ILiteralInteger
{
   int Literal { get; }
}

public class LiteralInt10 : ILiteralInteger
{
   public int Literal { get { return 10; } }
}

public class MyTemplateClass< L > where L: ILiteralInteger, new( )
{
   private static ILiteralInteger MaxRows = new L( );

   public void SomeFunc( )
   {
      // use the literal value as required
      if( MaxRows.Literal ) ...
   }
}

用法:

var myObject = new MyTemplateClass< LiteralInt10 >( );

答案 3 :(得分:5)

C#泛型专门用于运行时,而C ++模板在编译时处理以创建一个全新的类型。鉴于此,运行时根本没有处理非类型参数的功能(它不仅仅是C#问题)。

答案 4 :(得分:0)

以下方法可能有用,具体取决于您要完成的工作。基础集合的大小由抽象属性确定。

public abstract class TupleBase
{
    protected abstract int NumElems { get; }
    protected object[] values;

    protected TupleBase(params object[] init)
    {
        if (init.Length != NumElems)
        {
            throw new ArgumentException($"Collection must contains {NumElems} elements", nameof(init));
        }
        values = new object[NumElems];
        for (int i = 0; i < init.Length; i++)
        {
            values[i] = init[i];
        }
    }

    protected object Get(int idx) => values[idx];
    protected void Set(int idx, object value) => values[idx] = value;
}

public class Tuple1<T> : TupleBase {
    protected override int NumElems => 1;
    public Tuple1(T val0) : base(val0) { }
    public T Get0() => (T)Get(0);
    public void Set0(T value) => Set(0, value);
}

public class Tuple2<T, U> : TupleBase {
    protected override int NumElems => 2;
    public Tuple2(T val0, U val1) : base(val0, val1) { }
    public T Get0() => (T)Get(0);
    public U Get1() => (U)Get(1);
    public void Set0(T value) => Set(0, value);
    public void Set1(U value) => Set(1, value);
}

public class Tuple3<T, U, V> : TupleBase
{
    protected override int NumElems => 3;
    public Tuple3(T val0, U val1, V val2) : base(val0, val1, val2) { }
    public T Get0() => (T)Get(0);
    public U Get1() => (U)Get(1);
    public V Get2() => (V)Get(2);
    public void Set0(T value) => Set(0, value);
    public void Set1(U value) => Set(1, value);
    public void Set2(V value) => Set(2, value);
}

public class Tuple4<T, U, V, W> : TupleBase
{
    protected override int NumElems => 4;
    public Tuple4(T val0, U val1, V val2, W val3) : base(val0, val1, val2, val3) { }

    public T Get0() => (T)Get(0);
    public U Get1() => (U)Get(1);
    public V Get2() => (V)Get(2);
    public W Get3() => (W)Get(3);
    public void Set0(T value) => Set(0, value);
    public void Set1(U value) => Set(1, value);
    public void Set2(V value) => Set(2, value);
    public void Set3(W value) => Set(3, value);
}

public class Tuple5<T, U, V, W, X> : TupleBase
{
    protected override int NumElems => 5;
    public Tuple5(T val0, U val1, V val2, W val3, X val4) : base(val0, val1, val2, val3, val4) { }
    public T Get0() => (T)Get(0);
    public U Get1() => (U)Get(1);
    public V Get2() => (V)Get(2);
    public W Get3() => (W)Get(3);
    public X Get4() => (X)Get(4);
    public void Set0(T value) => Set(0, value);
    public void Set1(U value) => Set(1, value);
    public void Set2(V value) => Set(2, value);
    public void Set3(W value) => Set(3, value);
    public void Set4(X value) => Set(4, value);
}