强制派生类在其中实现/扩展特定结构

时间:2018-10-23 08:11:39

标签: c# oop inheritance

我应该通过添加更多类来扩展应用程序,作为一种“插件”。该应用程序使用字符串参数,并尝试实例化与该字符串相对应的名为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>

1 个答案:

答案 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
...
}