我应该通过添加更多类来扩展应用程序,作为一种“插件”。该应用程序使用字符串参数,并尝试实例化与该字符串相对应的名为class的对象。如果成功,则应用程序将继续从该类(例如Initialize()
)调用各种特定功能,并在其中序列化名为Data
*的结构或类。
理想情况下,一个这样的类如下所示:
public class SomePlugin
{
public abstract event ChangesAppliedDelegate ChangesApplied;
public delegate void ChangesAppliedDelegate();
public Model Data = new Model();
public SomePlugin()
{
Data.Initialized = false;
}
public bool Initialize()
{
Data.Initialized = true;
return true;
}
[Serializable]
public struct Model
{
bool Initialized;
}
}
根据本自述文件,每个此类都必须存在以下内容,因为该框架依赖于这些内容:
ChangesApplied
的事件。Data
的可序列化对象,其定义和内容无关。Initialize()
。我现在想要实现的是用Base类替换该自述文件,据称它将“指导”我或任何后续开发人员尽可能清楚地实现这些要求。现在,我只能定义数据模型。
我希望将该部分设为空,以便它确实存在于Base类中,并且框架可以依靠它,而所有派生类都可以选择用内容填充它:
public abstract class Base
{
public abstract Model Data;
public partial struct Model { }
}
public class Derivate : Base
{
public Model Data = new Model();
public bool Initialize()
{
Data.A = 1;
Data.B = "Hello world.";
return true;
}
public partial struct Model
{
int A;
string B;
}
}
但是,这给出了很多编译器错误和警告(例如,Derivate.Model隐藏了Base.Model(这不是我想要的),或者修饰符“抽象”在字段上无效。 / em>,建议我使用属性,无论如何,这都会使我进入getter和setters函数。实现我的想法的最佳方法是什么?我的想法有什么好处吗?还是我只是使事情变得比原来更加复杂?
更新
尽管这个问题一直搁置,但我得出的结论是,接口(而不是抽象类)更适合于此目的,但有两个主要缺点:
GetData()
和SetData()
,特别是GetData()
的广义返回类型(object
)暗示存在某种数据对象。但是,由于该框架不再直接与数据一起使用,因此问题如何以及在何处存储数据变得无关紧要(这就是为什么我在下面使用MyModel
而不是Model
的原因代码)。如下所示:
public interface IBase
{
object GetData();
void SetData(object newData);
}
public Derivate : IBase
{
public MyModel Data = new MyModel();
public bool Initialize()
{
Data.A = 1;
Data.B = "Hello world.";
return true;
}
public object GetData()
{
return Data;
}
public void SetData(object newData)
{
Data = (MyModel) newData;
}
[Serializable]
public struct MyModel
{
public int A;
public string B;
}
}
*对于那些在进行适当的重新编辑和澄清之前阅读问题的人:我混淆了Model
(结构定义)和Data
(Model的实例)的含义。 / p>
答案 0 :(得分:1)
这是我对这种情况的看法:
首先是插件合同:
public interface IModel
{
bool Initialized { get; }
}
public interface IPlugin<TModel> where TModel: struct, IModel
{
event ChangesAppliedDelegate<TModel> ChangesApplied;
TModel Model { get; }
void SetData(TModel model);
}
public delegate void ChangesAppliedDelegate<in TModel>(TModel model) where TModel : struct, IModel;
然后是合同的典型执行
public struct MyModel : IModel
{
/// <summary>
/// Data constructor. Assumes data is initialized after this is called.
/// </summary>
/// <param name="a">The A value</param>
/// <param name="b">The B value</param>
public MyModel(int a, string b)
{
this.A = a;
this.B = b;
this.Initialized = true;
}
/// <summary>
/// Copy construct, but with ability to flip the Initialized property
/// </summary>
/// <param name="other">The model to copy data from</param>
/// <param name="initialized">Set the initialization flag here</param>
public MyModel(MyModel other, bool initialized = true)
{
this = other;
this.Initialized = initialized;
}
public int A { get; }
public string B { get; }
public bool Initialized { get; }
}
public class MyModelPlugin: IPlugin<MyModel>
{
public MyModel Model { get; private set; }
public event ChangesAppliedDelegate<MyModel> ChangesApplied;
public void SetData(MyModel model)
{
if(model.Initialized)
{
this.Model =model;
ChangesApplied?.Invoke(Model);
}
else
{
// Handle case where model isn't initialized
}
}
}
最后是一个用法示例
static class Program
{
static void Main(string[] args)
{
var plugin = new MyModelPlugin();
plugin.ChangesApplied += (m) => Debug.WriteLine(m.Initialized ? $"A={m.A}, B={m.B}" : "Uninitialized");
// Uninitialized model
var model = new MyModel();
plugin.SetData(model);
// Initialized model
model = new MyModel(1, "Hello SO");
plugin.SetData(model);
// Make a copy and check equality
var copy = plugin.Model;
Debug.WriteLine($"Copy is identical = { copy == model }");
}
}
带有调试器的输出
A=1, B=Hello SO
Copy is identical = True
下面显示的每个模型结构所需的代码,以使框架正确处理结构。该结构必须是不可变的,并且必须根据数据处理相等运算符==
和!=
。最后,GetHashCode()
需要返回唯一的数据驱动值,以供在Dictionary
类型的容器中使用。
[ImmutableObject(true)]
public struct MyModel : IModel, IEquatable<MyModel>
{
...
#region Equality Comparison
public override bool Equals(object obj)
{
if(obj is MyModel model)
{
return Equals(model);
}
return false;
}
public bool Equals(MyModel other)
{
return A==other.A&&
B==other.B;
}
public override int GetHashCode()
{
var hashCode = -1817952719;
hashCode=hashCode*-1521134295+A.GetHashCode();
hashCode=hashCode*-1521134295+B.GetHashCode();
return hashCode;
}
public static bool operator ==(MyModel model1, MyModel model2)
{
return model1.Equals(model2);
}
public static bool operator !=(MyModel model1, MyModel model2)
{
return !(model1==model2);
}
#endregion
...
}