我有多个类需要存储。目前它们存储在json中,但这最终会改变。我想要序列化/存储代码,从类的实际代码分开,所以当我们,例如,从本地json文件移动到云SQL,基类不需要编辑。
我的计划是在新文件中添加ToJson方法作为扩展名。这正好符合要求。
我有一个尝试存储对象的JsonStorageManager。所有可以存储的项都实现了IStorable。
public interface IStorageManager{
public void Save();
}
public interface IStorable{
public string StorageKey; //in the event of SQL will keep ID, in the event of json is the path
}
public class JsonStorageManager : IStorageManager
{
public void Save(IStorable obj)
{
//here I need to check if the actual type of obj, actually has the required extension
//in order to be serialized;
JsonSerializer.SaveToFile(obj,StorageKey, obj. **ToJson()**) //path, string
//here is the issue since IStorable does not have .ToJson()
}
}
另一边要存储的类如下:
in file MyDataSet.cs:
public class MyDataSet : IStorable {
public string StorageKey = "filename.json";
}
in a file at StorageManagers/JsonStorageManagers/Extensions/MyDataSetJsonExtension.cs
static class MyDataSetExtension
{
public static string ToJson(this MyDataSet set)
{
return Json.Serialize.WhateverIsNeededHere(set);
}
}
计划是所有需要用Json保存的类型,创建扩展方法文件扩展名。通过这种方式,添加新类型既不需要编辑现有的MyDataSet类,也不需要编辑JsonStorageManager。请添加新文件。
如何访问扩展方法?我怎样才能建立一个合适的结构呢?
我如何构建整个事情,以便我可以做obj.ToJson(),然后如果实际对象没有实现扩展,则抛出异常。如果实际对象具有扩展名,则调用
如果您可以访问IStorable
,您应该大量考虑在那里实现它,例如:
interface IStorable
{
string Serialize();
}
如果由于某种原因您无法更改任何相关的类,那么扩展可能不是一个好的选择。扩展只不过是一个编译时的概念,它使在编写时更容易使用共享代码,任何在运行时动态查找它们的尝试都将是脆弱的,难以维护。
如果你仍然想继续在运行时动态寻找正确的扩展/序列化器的路线,你可以做一些不同的事情。我们可以使用反射或者只是简单的依赖注入,等等。
使用你的问题和评论中的上下文,我以一种不改变你已经定义的类的类或方法签名的方式写了这篇文章。
依赖注入
考虑注入某种形式的序列化器"查找器"进入IStorageManager
班。我再次建议你重新审视一下你已经拥有的接口,但是如果你没有能力改变这些,我写了一个例子,只是一个或多个东西,可能会得到你想要的结果。
void Main()
{
// create the group and register any serializers,
// has compile time saftey with the generic constraint of .Register<T>
// and generic constrains of Func<>
var serializerGroup = new SerializerGroup();
serializerGroup.Register<MyDataSet>(MyDataSetExtension.ToJson);
// inject it into the manager class so we dont have to change the signature
JsonStorageManager manager = new(serializerGroup);
}
public interface ISerializerGroup
{
/// Attempts to find a function that will serialize the provided object
public bool TryGetSerializer(IStorable obj, out Func<IStorable,string> serializer);
}
public class SerializerGroup : ISerializerGroup
{
private readonly IDictionary<Type, Func<IStorable, string>> serializers = new Dictionary<Type, Func<IStorable, string>>();
public bool TryGetSerializer(IStorable obj, out Func<IStorable, string> serializer)
{
Type type = obj?.GetType();
if (serializers.ContainsKey(type))
{
serializer = serializers[type];
return true;
}
serializer = default;
return false;
}
// we add compile time saftey here by constraining our generic types
public void Register<T>(Func<IStorable, string> serializer) where T : IStorable
{
if (serializer is null)
{
throw new ArgumentNullException(nameof(serializer));
}
serializers.Add(typeof(T),serializer);
}
}
public class JsonStorageManager : IStorageManager
{
private readonly ISerializerGroup serializers;
// instead of modifing this class' signature we can just inject the
// dependency and keep the "finder" implementation detail abstracted
public JsonStorageManager(ISerializerGroup serializers)
{
this.serializers = serializers ?? throw new ArgumentNullException(nameof(serializers));
}
public void Save(IStorable obj)
{
// try to get a serializer, if we fail throw an error
if (serializers.TryGetSerializer(obj, out Func<IStorable,string> serializer) is false)
{
throw new NotSupportedException("Some meaningful error here");
}
JsonSerializer.SaveToFile(obj, obj.StorageKey, serializer.Invoke(obj));
}
}