C # genérico con constante

¿Hay algo similar a esta plantilla de C ++?

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

Quiero que cada instancia de B <1>, B <2>, etc. (por ejemplo, tupla) sea de un tipo diferente.

preguntado el 08 de enero de 11 a las 22:01

5 Respuestas

La respuesta corta es no.

No se ajusta a la forma en que funcionan los genéricos de C #, en oposición a las plantillas de C ++.

Los genéricos .net no son una característica de lenguaje, son una característica de tiempo de ejecución. El tiempo de ejecución sabe cómo crear instancias de genéricos a partir de un código de bytes genérico especial que es bastante restringido en comparación con lo que pueden describir las plantillas de C ++.

Compare esto con las plantillas de C ++, que básicamente crean una instancia de todo el AST de la clase utilizando tipos sustituidos. Sería posible agregar instanciación basada en AST al tiempo de ejecución, pero ciertamente sería mucho más complejo que los genéricos actuales.

Sin características como matrices de tipo valor (que solo existen en código inseguro), la creación de instancias de plantilla recursiva o la especialización de plantilla utilizando tales parámetros tampoco sería muy útil.

Respondido 23 Abr '16, 12:04

Bueno, en tiempo de ejecución (JIT) .Net crea la instanciación. La instanciación es esencialmente una duplicación con modificaciones. No me opondría tanto a AST como a MSIL. Representación diferente de conceptos similares. - Kirill Kobelev

Una solución a esta limitación es definir una clase que, en sí misma, exponga el valor literal que le interesa. Por ejemplo:

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 ) ...
   }
}

Uso:

var myObject = new MyTemplateClass< LiteralInt10 >( );

Respondido el 21 de Septiembre de 15 a las 15:09

C # no admite parámetros genéricos que no sean de tipo como lo hace C ++.

Los genéricos de C # son mucho más simples y menos capaces que las plantillas de C ++. MSDN tiene una lista sucinta de Diferencias entre plantillas de C ++ y genéricos de C #.

Respondido el 09 de enero de 11 a las 02:01

Sin embargo, la información adicional se puede "codificar" en tipos (por definición de contrato o de otro modo), es fácil de ver con un conjunto fijo de tipos discretos (p. Ej. Vn : V, where Vn.Value = n for all Vn), pero también hay números de la iglesia no es que yo recomiendo cualquier enfoque aquí :-) - user166390

No es una respuesta incorrecta, pero sería bueno si proporcionara un enlace a los genéricos de C # y / o hablara sobre declaración genérica / "instanciación" frente a instanciación de plantilla. - usuario166390

@pst: Claro; puede codificar la información en tipos. También puede evitar los genéricos por completo y escribir muchas clases no genéricas. En todo caso; Agregué un enlace al artículo de MSDN que enumera las principales diferencias entre las plantillas y los genéricos. - James McNellis

Los genéricos de C # están especializados en tiempo de ejecución, mientras que las plantillas de C ++ se procesan en tiempo de compilación para crear un tipo completamente nuevo. Dado esto, el tiempo de ejecución simplemente no tiene las características para procesar parámetros que no son de tipo (no es solo un problema de C #).

Respondido el 09 de enero de 11 a las 01:01

El siguiente enfoque puede ser útil según lo que intente lograr. El tamaño de la colección subyacente está determinado por una propiedad abstracta.

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

contestado el 28 de mayo de 20 a las 22:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.