接口和对象反序列化

时间:2017-03-18 04:36:40

标签: c# oop interface

我有一个接口,它定义了一个可序列化为字节数组的类。

public interface IByteSerializable
{
    byte[] GetBytes();
}

这是一个自然的合作伙伴,它是一个反序列化方法,我想返回一个实现IByteSerializable的对象。

我正在研究如何设计这样的界面。

这似乎没有意义:

public interface IByteSerializable
{
    byte[] GetBytes();
    IByteSerializable GetObject(byte[] bytes);
}

因为GetObject()的实现不能是static而使用虚拟IByteSerializable对象只是为了调用GetObject()方法来反序列化是没有意义的我正在追求的实际对象。

这样做似乎没有意义:

public interface IByteSerializableFactory
{
    IByteSerializable GetObject(byte[] bytes);
}

Factory类可以解决问题,但这感觉会导致类爆炸。此外,给定IByteSerializable子类如何序列化然后反序列化的细节是相互依赖的,因此将它们保持在同一位置是有意义的,而不是在两个不同的类中。显然,反序列化给定IByteSerializable对象所需的确切过程完全取决于该对象的GetBytes()方法是如何编写的。

我可以使用常见的设计或模式来解决这个问题吗?

2 个答案:

答案 0 :(得分:2)

使用通用接口,每个实现都可以关闭泛型并返回闭合类型。决定要返回的类型取决于实现。

public interface ICustomSerializable<T> where T : class
{
    byte[] GetBytes();
    T Deserialize(byte[]);
}

public class Foo : ICustomSerializable<Foo>
{
    public byte[] GetBytes() {} 
    public Foo Deserialize(byte[]){} 
} 

public class Bar : ICustomSerializable<Bar>
{
    public byte[] GetBytes() {} 
    public Bar Deserialize(byte[]){} 
} 

如果你有以通用方式进行序列化的类,那么:

public abstract class Something
{
    public byte[] GetBytes() { //common code } 
} 

public class SomethingConcrete : Something, ICustomSerializable<SomethingConcrete>
{
    public SomethingConcrete Deserialize(byte[]){} 
} 

答案 1 :(得分:2)

当涉及到你的问题时,对接口,类和模式有很多不同的看法。我的个人首选项将实现一个带有byte []属性的接口和一个带有虚方法的抽象类(甚至完全丢失接口,这对您来说可能不是一个选项DI和单元测试不能很好地发挥):

public interface IByteSerializable
{
    byte[] SerializableByteObject { get; }
}

public abstract class ByteSerializable : IByteSerializable
{
    public byte[] SerializableByteObject { get; }
    protected virtual byte[] GetBytes() {
        return SerializableByteObject;
    }
    public abstract IByteSerializable GetObject();
    //{    // You can make this method virtual and use Impl method:
           // GetObjectImpl(SerializableByteObject);
    //}
    protected internal IByteSerializable GetObjectImpl(byte[] bytes) {
        // If you need a default implementation (GetObject() should be protected virtual then)
        // return IByteSerializable...;
    }
}

我想强调,VS抽象类的接口是一个无休止的讨论。如果你可以在没有实现接口的情况下完成任务而只使用抽象类 - 我强烈建议你这样做。

更新3/18/17 :回复评论(定义行为是界面的目的)并解释我如何看待它添加以下说明。

在这种情况下,我们定义的“行为”是“对象应该可以转换为字节数组。转换结果应该可以转换回同一个对象。”所以我们实际上是为一个对象和一个字节数组定义行为(因为在一个对象被反序列化之后 - 它不再是同一个对象,它只是一个字节数组。)

从我的角度来看,这是纯粹的工厂模式场景。

// Let's define an interface for our serializable type of objects factory
public interface IByteSerializableFactory<T>
{
     T CreateFromBytes(byte[] objectDataToUse);
     byte[] CovertToBytes(T objectToConvert);
}

// Interface for any class that needs a serialization factory
// This is not even necessary, but I like it to enforce people to implement simple methods that reference the factory.

public interface IByteSerializable<T>
{
    IByteSerializableFactory<T> GetFactory();
}

// Now a moment comes for us to have this kind of class. We need to build a factory first (because our interface requires a GetFactory() implementation. We can lose the IByteSerializable interface altogether, but then we lose a way to let people know which factory should be used.

public class SomeBaseClassSerializationFactory : IByteSerializableFactory<SomeBaseClass>
{
    public SomeBaseClass CreateFromBytes(byte[] objectDataToUse) { //...
        return new SomeClass();
    }
    public byte[] CovertToBytes(SomeBaseClass objectToConvert) { //...
        return new byte[1];
    }
}

// We have a factory, let's implement a class.

public abstract class SomeBaseClass : IByteSerializable<SomeBaseClass>
{
    public virtual IByteSerializableFactory<SomeBaseClass> GetFactory() { 
        return new SomeBaseClassSerializationFactory();                                             
    }
}

public class SomeClass : SomeBaseClass {
    // Now we're independent. Our derived classes do not need to implement anything.
    // If the way the derived class is serialized is different - we simply override the method
}

更新2 3/18/17 :以不同的答案回复评论(简单使用界面的通用实施)。

不幸的是,没有干净的方法可以做到这一点。通过使用一些作弊,定义类来定义序列化方法并使用反射来返回正确的类型,有一种肮脏的(我的个人观点:“BAD BAD BAD!”)方式。下面的示例将在序列化方法中使用大量自定义逻辑,以使用具有不同类型的正确字段:

// You define an enum with action and a dictionary with a collection of serialization methods.
public enum SerializationAction {
    ToBytes,
    ToObject    
}

// It can also be an enum, but it's easier to test with a collection of strings.
public static readonly string[] SerializationKindList = new string[] {
    "FirstKind",
    "SecondKind"
};

// This generic class can have an implementation of all the handlers. Additional switching can be done by type, or reflection can be used to find properties for different classes and construct different classes.
public class SerializationMethod {
    public object ProcessByKind (string kindToUse, SerializationAction action, object objectToProcess) {
        if (kindToUse == "FirstKind") {
            if (action == SerializationAction.ToBytes) {
                return new byte[1]; 
            }

            return new SomeClass(); // These would need to be your hard implementations. Not clean.
        } else {
            throw new NotImplementedException();    
        }
    }
}

// This struct type defines the serialization method and is required for the interface implementation
public struct ByteSerialization
{
    public string SerializationTypeName { get; private set; }
    public ByteSerialization(string kindToUse) {
        if (!SerializationKindList.Contains(kindToUse)) {
            throw new ArgumentException();
        }

        SerializationTypeName = kindToUse;
    }
    public byte[] Deserialize(object objectToProcess) {
        var serializationMethod = new SerializationMethod();
        return (byte[])serializationMethod.ProcessByKind(this.SerializationTypeName, SerializationAction.ToBytes, objectToProcess);
    }
    public object Serialize(byte[] byteArrayToProcess) {
        var serializationMethod = new SerializationMethod();
        return serializationMethod.ProcessByKind(this.SerializationTypeName, SerializationAction.ToObject, byteArrayToProcess);
    }
}


// Interface for any class that needs to use generic serialization
public interface IByteSerializable
{
    ByteSerialization serializationType { get; }
}

// Creating extension methods for the interface to make the life easier
public static class IByteSerializableExtensions {
    public static byte[] DeserializeObjectIntoBytes(this IByteSerializable objectToProcess) {
        return objectToProcess.serializationType.Deserialize(objectToProcess);
    }
    public static void SerializeObjectFromBytes(this IByteSerializable objectToProcess, byte[] fromBytes) {
        var someObjectData = objectToProcess.serializationType.Serialize(fromBytes);
    }
}


// Abstract base class implementation with static readonly field.
// Only downside - there is no way to enforce the config of this field in the constructor from the interface.
// There also no way to make sure this field always gets set for other implementations of IByteSerializable
public abstract class SomeBaseClass : IByteSerializable
{
    private static readonly ByteSerialization _serializationType = new ByteSerialization("FirstKind");

    public ByteSerialization serializationType { get { return _serializationType; } }
}

public class SomeClass : SomeBaseClass {

}


// And here's how one would use it. You will need to create a new object of the class before serializing from bytes.
var someClass = new SomeClass();
var bytes = someClass.DeserializeObjectIntoBytes();
var someClass2 = new SomeClass();
var byteArray = new byte[1];
someClass2.SerializeObjectFromBytes(byteArray);