C# 泛型常量

发布于 2024-10-11 05:08:27 字数 181 浏览 1 评论 0原文

有没有类似这个C++模板的东西?

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

我想让 B<1>、B<2> 等(例如元组)的每个实例成为不同的类型。

Is there something similar to this C++ template?

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

I want to make every instance of B<1>, B<2>, etc (eg tuple) a different type.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

度的依靠╰つ 2024-10-18 05:08:27

简短的回答是

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

.net 泛型不是语言功能,而是运行时功能。运行时知道如何从特殊的泛型字节码实例化泛型,与 C++ 模板可以描述的内容相比,这是相当有限的。

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

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

The short answer is no.

It doesn't fit the way C# generics, as apposed to C++ templates, work.

The .net generics are not a language feature, they are a runtime feature. The runtime knows how to instantiate generics from special generic bytecode which is rather restricted compared to what C++ templates can describe.

Compare this with C++ templates, which basically instantiate the whole AST of the class using substituted types. It'd be possible to add AST based instantiation to the runtime, but it'd certainly be much more complex than the current generics.

Without features like value-type arrays (which only exist in unsafe code), recursive template instantiation or template specialization using such parameters wouldn't be very useful either.

合约呢 2024-10-18 05:08:27

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

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

A workaround to this limitation is to define a class which itself exposes the literal value you are interested in. For example:

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

Usage:

var myObject = new MyTemplateClass< LiteralInt10 >( );
孤单情人 2024-10-18 05:08:27

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

C# 泛型比 C++ 模板简单得多,功能也少得多。 MSDN 提供了C++ 模板和 C# 泛型之间的差异的简洁列表。

C# does not support non-type generic parameters like C++ does.

C# generics are far simpler and less capable than C++ templates. MSDN has a succinct list of Differences Between C++ Templates and C# Generics.

冷夜 2024-10-18 05:08:27

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

C# generics are specialized at run-time, whereas C++ templates are processed at compile-time to make an entirely new type. Given this, the runtime simply doesn't have the features to process non-type parameters (it's not just a C# issue).

扶醉桌前 2024-10-18 05:08:27

对于 C# 10,有些东西有点接近:

interface IConstant
{
    abstract static int Constant { get; }
}

class B<T> where T : IConstant
{
    int GetConstant() => T.GetConstant();
}

class Constant1 : IConstant { static int Constant => 1; }
class Constant2 : IConstant { static int Constant => 2; }

遗憾的是,这并不能保证 Constant 实际上是常量,并且您需要为要使用的所有数字定义类型。但是,它确实允许您区分 BB

几乎不等价,但至少有一些

注意,您需要添加 ;true 到您的 .csproj 才能正常工作,因为 abstract static 接口方法仍处于预览状态

With C# 10, there's something that comes sort of close:

interface IConstant
{
    abstract static int Constant { get; }
}

class B<T> where T : IConstant
{
    int GetConstant() => T.GetConstant();
}

class Constant1 : IConstant { static int Constant => 1; }
class Constant2 : IConstant { static int Constant => 2; }

This does not guarantee that Constant is actually constant sadly, and you need to define types for all numbers you want to use. However it does allow you to discriminate B<Constant1> and B<Constant2>

Not nearly an equivalent but something at least

Note that you need to add <EnablePreviewFeatures>true</EnablePreviewFeatures> to your .csproj for this to work, as abstract static Interface methods are still in preview

动次打次papapa 2024-10-18 05:08:27

根据您想要实现的目标,以下方法可能有用。底层集合的大小由抽象属性决定。

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

The following approach may be useful depending on what you're trying to accomplish. The size of the underlying collection is determined by an abstract property.

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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文