C#泛型:投泛型类型,以价值型(C# generics: cast generic type to

2019-08-22 02:27发布

我有一个通用的类指定类型T的值可以是一个int,UINT,双或浮动保存值。 现在,我想要得到的值的字节其编码为一个特定的协议。 因此,我想使用的方法BitConverter.GetBytes()可惜Bitconverter不支持泛型类型或不确定的对象。 这就是为什么我要投的价值和调用GetBytes会的)具体的过载(。 我的问题:我如何可以投一个通用的价值为int,double或float? 这不起作用:

public class GenericClass<T>
    where T : struct
{
    T _value;

    public void SetValue(T value)
    {
        this._value = value;
    }

    public byte[] GetBytes()
    {
        //int x = (int)this._value;
        if(typeof(T) == typeof(int))
        {
            return BitConverter.GetBytes((int)this._value);
        }
        else if (typeof(T) == typeof(double))
        {
            return BitConverter.GetBytes((double)this._value);
        }
        else if (typeof(T) == typeof(float))
        {
            return BitConverter.GetBytes((float)this._value);
        }
    }
}

是否有投的一般价值的可能性? 还是有另一种方式来获得字节?

Answer 1:

首先,这是一个非常糟糕的代码味道。 任何你正在做的这样的赔率类型参数类型测试时间是好你滥用仿制药。

C#编译器知道你以这种方式滥用仿制药和铸造价值,你将它转换为int之前反对禁止从类型T的值强制转换为int等,您可以关闭编译器在你的方式:

return BitConverter.GetBytes((int)(object)this._value);

呸。 再次,这将更好地找到另一种方式来做到这一点。 例如:

public class NumericValue
{
    double value;
    enum SerializationType { Int, UInt, Double, Float };
    SerializationType serializationType;        

    public void SetValue(int value)
    {
        this.value = value;
        this.serializationType = SerializationType.Int
    }
    ... etc ...

    public byte[] GetBytes()
    {
        switch(this.serializationType)
        {
            case SerializationType.Int:
                return BitConverter.GetBytes((int)this.value);
            ... etc ...

没有泛型必要的。 储备仿制药是实际上通用的情况。 如果你写的代码四次一个为每种类型的,你还没有获得与仿制药什么。



Answer 2:

好了,这让我感到类型真的是无法正常通用的开始:它只能是少数类型之一,你不能表达的约束。

然后,你要拨打的不同过载GetBytes根据类型T 。 泛型不会为这种事情很好地工作。 你可以使用动态类型来实现它,在.NET 4及以上:

public byte[] GetBytes()
{
    return BitConverter.GetBytes((dynamic) _value);
}

......但同样,这并不觉得自己是一个不错的设计。



Answer 3:

漂亮的晚了答案,但反正...有一种方法,使其更好略做......以这种方式使用泛型:实现另一个泛型类型,其将该类型的为您服务。 所以,你不必在意拆箱,铸造对象的类型等等都可以正常运行。

此外,在您的GenericClass,现在你不必切换类型,你可以使用IValueConverter<T>也将它转换as IValueConverter<T> 这样一来,仿制药将做的魔力为你找到正确的接口实现,此外,对象将是无效的,如果T是什么你不支持...

interface IValueConverter<T> where T : struct
{
    byte[] FromValue(T value);
}

class ValueConverter:
    IValueConverter<int>,
    IValueConverter<double>,
    IValueConverter<float>
{
    byte[] IValueConverter<int>.FromValue(int value)
    {
        return BitConverter.GetBytes(value);
    }

    byte[] IValueConverter<double>.FromValue(double value)
    {
        return BitConverter.GetBytes(value);
    }

    byte[] IValueConverter<float>.FromValue(float value)
    {
        return BitConverter.GetBytes(value);
    }
}

public class GenericClass<T> where T : struct
{
    T _value;

    IValueConverter<T> converter = new ValueConverter() as IValueConverter<T>;

    public void SetValue(T value)
    {
        this._value = value;
    }

    public byte[] GetBytes()
    {
        if (converter == null)
        {
            throw new InvalidOperationException("Unsuported type");
        }

        return converter.FromValue(this._value);
    }
}


Answer 4:

你可能使用Convert.ToInt32(this._value)(int)((object)this._value) 但总的来说,如果你发现自己有一个通用的方法来检查特定类型的,还有你的设计有问题。

在你的情况,你可能应该考虑一个抽象基类,然后对你要使用类型派生类:

public abstract class GenericClass<T>
where T : struct
{
    protected T _value;

    public void SetValue(T value)
    {
        this._value = value;
    }

    public abstract byte[] GetBytes();
}

public class IntGenericClass: GenericClass<int>
{
    public override byte[] GetBytes()
    {
        return BitConverter.GetBytes(this._value);
    }
}


Answer 5:

如果你唯一的目标是到GetBytes方法添加到这些类型的,是不是一个好得多的解决方案将其添加为扩展方法,如下所示:

public static class MyExtensions {
    public static byte[] GetBytes(this int value) {
        return BitConverter.GetBytes(value) ;
    }
    public static byte[] GetBytes(this uint value) {
        return BitConverter.GetBytes(value) ;
    }
    public static byte[] GetBytes(this double value) {
        return BitConverter.GetBytes(value) ;
    }
    public static byte[] GetBytes(this float value) {
        return BitConverter.GetBytes(value) ;
    }
}

如果你真的很需要你的通用类用于其他目的,只是做肮脏的“双类型转换”像埃里克提到,你的类型转换值第一个对象。



Answer 6:

什么会GenericClass<DateTime>吗? 相反,它似乎你有一组离散的,其知道如何让自己的字节,所以让一个抽象基类,做所有常见的工作,然后进行3,其覆盖方法来指定之间改变的一件具体类课程他们:

public abstract class GenericClass<T>
{
    private T _value;

    public void SetValue(T value)
    {
        _value = value;
    }

    public byte[] GetBytes()
    {
        return GetBytesInternal(_value);
    }

    protected abstract byte[] GetBytesInternal(T value);
}

public class IntClass : GenericClass<int>
{
    protected override byte[] GetBytesInternal(int value)
    {
        return BitConverter.GetBytes(value);
    }
}

public class DoubleClass : GenericClass<double>
{
    protected override byte[] GetBytesInternal(double value)
    {
        return BitConverter.GetBytes(value);
    }
}

public class FloatClass : GenericClass<float>
{
    protected override byte[] GetBytesInternal(float value)
    {
        return BitConverter.GetBytes(value);
    }
}

这不仅提供了干净,强类型的三种已知类型的实现,但敞开了大门对其他任何人继承Generic<T>并提供相应的实现的GetBytes



Answer 7:

迟到了,但只是想上的评论说,原来的建议是一个“糟糕的设计”评论 - 在我看来,原来的建议(虽然它不工作)是不是“必然”一个糟糕的设计可言!

从一个强大的C ++未来(14年3月11日)背景模板元编程的深刻理解,我创建了C ++ 11用最少的代码重复类型的通用串行化库(我们的目标是有不重复的代码,我相信我已经实现了它的99%)。 由C ++ 11提供的,虽然可能会变得复杂的编译时间模板元编程工具,有助于实现真正的通用型实现系列化库。

然而,这是非常不幸的是,当我想C#实现一个简单的序列化框架,我正好被卡在了OP已经公布的问题。 在C ++中,模板类型T可以完全“转发”,以使用的部位,而C#泛型不转发实际编译时间类型到使用现场 - 任何第二(或更多)级参考到一个通用的类型T使得Ť成为一个独特类型,是不是可用在所有在实际使用的网站,从而GetBytes会(T)不能确定它应该调用一个特定的类型化超载 - 糟糕的是,即使是在C#中没有很好的方式,说:嘿,我知道ŧ类型为int,如果编译器不知道它,它“(INT)T”让一个int?

此外,而不是指责,基于型开关有坏设计的味道 - 这是一个伟大的用词不当,每当人们正在做一些先进型的框架,并已诉诸于基于类型的开关因无力的语言环境,没有真正了解手头的实际问题的约束,人们开始大胆地说型的开关是一个不好的设计 - 这是,对于大多数传统的面向对象的使用情况,但也有特殊情况,大部分的时间先进的使用情况一样的问题,我们这里所说的是,这是必要的。

还值得一提的是,我真的怪了BitConverter类被设计在传统的和不称职的方式,以满足一般的需求:而不是定义每个类型的类型特定的方法对于“GetBytes会”,也许这将是更通用友好定义GetBytes会的通用版本(T值) - 有一些限制可能,因此用户一般类型T可以被转发和按预期方式工作没有任何类型的开关在所有! 这同样适用于所有的ToBool / ToXxx方法一样 - 如果.NET框架提供了设施非通用版本,怎么会期望一个通用框架试图利用这个基础架构 - 型开关,或者如果没有式开关,你最终复制了每个数据的代码逻辑输入您要序列化 ​​- 哦,我想有一天我用C ++ TMP的工作,我只写了系列化的逻辑一次,几乎无限数量的类型,我可以支持。



文章来源: C# generics: cast generic type to value type