使用C#泛型如何将一个值特化?

4

我有一些实现基本Pixel类的C++代码。这里只是关键部分的桩代码:

// Pixel.h
template <class SizeType>
struct Pixel
{
public:
    typedef SizeType Size;
    Size R;
    Size G;
    Size B;
    static Size Min;
    static Size Max;

    TPixel(Size R = Min, Size G = Min, Size B = Min);
};

#include "Pixel.impl"


// Pixel.impl
template <class SizeType> SizeType Pixel<SizeType>::Min = std::numeric_limits<Size>::min();
template <class SizeType> SizeType Pixel<SizeType>::Max = std::numeric_limits<Size>::max();
template <> float Pixel<float>::Min = 0;
template <> float Pixel<float>::Max = 1;
template <> double Pixel<double>::Min = 0;
template <> double Pixel<double>::Max = 1;

我正在尝试在C#中复制这个:

struct Pixel<Size> where Size : struct
{
    public Size R;
    public Size G;
    public Size B;
    public static const Size Min; // What should I do here?
    public static const Size Max;
}

除了我不知道如何将Min / Max大小包含到类型中。我想能够拥有统一的像素接口,使您可以进行夹紧、添加、缩放等操作。

当我试图处理可能具有任意类型的向量和矩阵时,我遇到了类似的解决方案。有人能建议我如何完成上面的C ++代码吗?


如果我理解正确,您想知道如何在C#中进行模板特化吗? - James
泛型只能在类型之间使用,而不能在值之间使用。 - user166390
2
通用参数类型应该以大写字母T开头,如果你希望遵循 Microsoft 命名约定,例如 Pixel<TSize>。请参阅此处的指南:http://msdn.microsoft.com/zh-cn/library/ms229040.aspx - Lukazoid
尽管C#在计算几何或数值分析方面并不是非常适用,但我希望未来会有更多改进! - Salvatore Previti
你真的需要同时使用 doublefloat 类型吗?我认为OpenGL已经统一使用 float。如果你做出类似的决定,它将使你的接口更简单,并且可能会有性能上的好处。 - Merlyn Morgan-Graham
显示剩余2条评论
5个回答

3
与 C++ 的模板特化不同,.Net 中不存在泛型的特化。
除非你有一个约束条件,否则你不能将 T 实例初始化为除 T 之外的任何内容,因此无法使用静态构造函数来解决这个问题。
据我所知,没有任何约束条件允许你只指定数字类型或者具有从数字类型进行转换的类型。
我建议你只创建两种不同类型的类。
public struct PixelFloat
{
    public float R { get; set; }
    public float G { get; set; }
    public float B { get; set; }
    public const float Min = 0.0f;
    public const float Max = 1.0f;
}

public struct PixelDouble
{
    public double R { get; set; }
    public double G { get; set; }
    public double B { get; set; }
    public const double Min = 0.0f;
    public const double Max = 1.0f;
}

实际上,这和在底层编译后的结果是一样的。与此相比,其他解决方案并没有多大优势,因为您仍然需要输入类型:Pixel<double> Pixel<float>

另外,在这种情况下,我建议您使用显示使用泛型参数的名称。 Size并不明显是一个泛型参数,而TSize则是。但是TSize并没有描述类型的作用,它只描述了它如何变化。代替的方法是将其命名为TValue之类的名称。


@Merlyin Morgan-Graham - 为什么不把您的评论包含在您的回答中呢? :/ - Polity
@Polity:好的。虽然感觉它不是直接相关的,但我还是会将其移入答案中。 - Merlyn Morgan-Graham

1
唯一的方法是将接口约束添加到Size,要求Size具有MaxMin方法或类似的内容。当然,在这种情况下,定义将不再必要。

0
你需要做类似于这样的事情:
static class Pixel
{
    public static Size Min<Size>() where Size : struct;
    public static Size Max<Size>() where Size : struct;
}

struct Pixel<Size> where Size : struct
{
    public Size R;
    public Size G;
    public Size B;
}

然后像这样调用它:

Pixel<double> doublePixel;
var max = Pixel.Max<double>();

这对你来说行吗?


0

像这样的东西,也许?

public struct Pixel<Size> where Size : struct
{
    private enum DEF_TYPES
    {
        MIN,
        MAX
    }

    private static Dictionary<Type, Dictionary<DEF_TYPES, object>> m_Defs = new Dictionary<Type, Dictionary<DEF_TYPES, object>>
    {
        {
            typeof(float),
                new Dictionary<DEF_TYPES, object> {
                    {DEF_TYPES.MIN, 0f},
                    {DEF_TYPES.MAX, 1f}
                }
        },

        {
            typeof(double),
                new Dictionary<DEF_TYPES, object> {
                    {DEF_TYPES.MIN, 0d},
                    {DEF_TYPES.MAX, 1d}
                }
        }
    };

    private static Size GetValue(DEF_TYPES def_type)
    {
        if(!m_Defs.ContainsKey(typeof(Size)))
            throw new ArgumentException("No default values for template " + typeof(Size));
        if(!m_Defs[typeof(Size)].ContainsKey(def_type))
            throw new ArgumentException("No default value '" + def_type + "' for template " + typeof(Size));
        return (Size)m_Defs[typeof(Size)][def_type];
    }

    public Size R;
    public Size G;
    public Size B;
    public static readonly Size Min = GetValue(DEF_TYPES.MIN);
    public static readonly Size Max = GetValue(DEF_TYPES.MAX);
}

作为使用:

Console.WriteLine(Pixel<float>.Max);
Console.WriteLine(Pixel<float>.Min);
Console.WriteLine(Pixel<double>.Max);
Console.WriteLine(Pixel<double>.Min);

而且;

Pixel<double>.Min = 4;

不会编译,意料之中。

不是最理想的,但可以工作。


可以这样做。有点奇怪,但是解决了问题,并且尽可能接近原始界面。 - Merlyn Morgan-Graham

0

假设您想要实现的是在C#泛型中进行模板特化,这是不可能的。

这里有一个MSDN链接,比较了两者之间的差异,其中重点是:

  • C#不支持显式特化;也就是说,无法为特定类型自定义实现模板。
  • C#不支持部分特化:即针对类型参数子集的自定义实现。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接