在C#中为可序列化类型创建接口?

时间:2015-07-28 06:23:14

标签: c# interface

在Haskell中,我们可以这样做:

class Binary a where
    encode :: a -> ByteString
    decode :: ByteString -> a

instance Binary Int where
    encode x = ...
    decode bytes = ...

这定义了Binary实现的接口Int。我们现在可以将Int转换为字节数组,反之亦然。我希望在C#中实现相同的功能,我的第一直觉就是创建接口:

interface Binary<T> {
    byte[] Encode(T);
    static T Decode(byte[]);
}

但这失败了,因为Decode不允许static

我怎样才能尽可能干净地在C#中实现它?

请注意,我不想要一个可以创建一个空的&#34;或部分初始化T,然后通过调用非static Decode填充它:这将是混乱的并且打开一个窗口,在此期间对象的使用是潜在的错误来源

谢谢!

3 个答案:

答案 0 :(得分:2)

没有真正的方法可以直接将 static 方法添加到接口中,但是可以做的是将此方法添加到扩展类中或者在派生类中实现它(这几乎都是我们可以使用 static 方法):

但是在这种情况下我会做什么(如果由于某种原因我选择不使用标准的.Net序列化框架)我可能会创建自己的序列化程序类:

interface ISerializer<T> : IDisposable
{
   virtual byte[] Serialize(T instance); 
   virtual T DeSerialize(byte[] stream);
}

Class MySerializer<T> : ISerializer<T>
{
   public override byte[] Serialize(object instance)
   {
      // .. serialization logic
   }

   public override T DeSerialize(byte[] stream)
   {
      // .. deserialization logic
   }

   public void Dispose()
   {
      // .. dispose all managed resources here
   }
}

class MyClass
{
}

用法:

MyClass instance = new MyClass();
MyClass newInstance = null;    

using(ISerializer<MyClass> serializer = new MySerializer<MyClass>())
{
    bytes[] bytes = serializer.Serialize(instance);    
    newInstance = serializer.DeSerialize(bytes);
}

答案 1 :(得分:1)

我通常最终会将这样的代码分成两种类型 - 一种是基类或接口(取决于我的需要),通常是通用的,它代表实际的数据,一种是带有静态和扩展方法的静态助手类。在这个例子中,我可能会做这样的事情:

public interface IBinary<T>
{
    byte[] Encode(); // Alterantive definition as extension method below
}

public static class Binary
{
    public static T Decode<T>(byte[] bytes) where T : IBinary<T>¨
    {
        // deserialization logic here
    }

    // If you want, you can define Encode() as an extension method instead:
    public static byte[] Encode<T, TBinary>(this TBinary binary)
        where TBinary : IBinary<T>
    {
        // serialization logic here
    }
}

现在,您将通过创建类似

的类来使用此层次结构
public class BinaryEncodableInteger : IBinary<int>
{
    // must have this if defined in interface,
    // but if defined as extension method you get it for free
    public byte[] Encode()
    {
        // serialization logic here
    }
}

并将该课程用作

var binInt = new BinaryEncodableInteger();
var bytes = binInt.Encode();
var decoded = Binary.Decode<BinaryEncodableInteger>(bytes);

答案 2 :(得分:0)

我认为interface在C#和Haskell之间的差异太大,因为它在两种情况下都解决了这个问题。我宁愿使用implicit operators。虽然他们没有提供这个问题的通用解决方案,但据我所知,因为他们也通过静态方法工作。

我定义了一个简单的Binary类,其中包含byte数组,以及来自int的转换运算符。

class Binary
{
    public Binary(byte[] value)
    {
        this.Value = value.ToArray();
    }

    public byte[] Value { get; private set; }

    // User-defined conversion from Binary to int 
    public static implicit operator int(Binary b)
    {
        return b.Value[0] + (b.Value[1] << 8) + (b.Value[2] << 16) + (b.Value[3] << 24);
    }
    //  User-defined conversion from int to Binary
    public static implicit operator Binary(int i)
    {
        var result = new byte[4];
        result[0] = (byte)(i & 0xFF);
        result[1] = (byte)(i >> 8 & 0xFF);
        result[2] = (byte)(i >> 16 & 0xFF);
        result[3] = (byte)(i >> 24 & 0xFF);

        return new Binary(result);
    }
}

使用NUnit我可以按如下方式测试:

[TestCase(5, 0x05, 0x00, 0x00, 0x00)]
[TestCase(1500, 0xDC, 0x05, 0x00, 0x00)]
public void TestConvert(int i, byte b0, byte b1, byte b2, byte b3)
{
    Binary testBinary = i;
    Assert.AreEqual(b0, testBinary.Value[0]);
    Assert.AreEqual(b1, testBinary.Value[1]);
    Assert.AreEqual(b2, testBinary.Value[2]);
    Assert.AreEqual(b3, testBinary.Value[3]);

    int testInt = new Binary(new[] { b0, b1, b2, b3 });

    Assert.AreEqual(testInt, i);
}

你可以去实现你自己的特定逻辑,但这应该证明原理。