将不透明对象返回给调用者,而不违反类型安全性

时间:2010-05-27 15:26:56

标签: c# type-safety

我有一个应该返回当前状态快照的方法,以及另一个恢复该状态的方法。

public class MachineModel
{
    public Snapshot CurrentSnapshot { get; }
    public void RestoreSnapshot (Snapshot saved) { /* etc */ };
}

状态Snapshot类应该对调用者完全不透明 - 没有可见的方法或属性 - 但是它的属性必须在MachineModel类中可见。我显然可以通过向下转换来做到这一点,即让CurrentSnapshot返回object,并让RestoreSnapshot接受一个object参数,它会转回Snapshot

但强迫这样的铸造让我觉得很脏。什么是最好的替代设计,允许我既是类型安全的又是不透明的?

使用解决方案更新

我结束了接受的答案和关于接口的建议。 Snapshot类是一个公共抽象类,在MachineModel内有一个私有实现:

public class MachineModel
{
    public abstract class Snapshot
    {
        protected internal Snapshot() {}
        abstract internal void Restore(MachineModel model);
    }

    private class SnapshotImpl : Snapshot
    {
        /* etc */
    }

    public void Restore(Snapshot state)
    {
        state.Restore(this);
    }
}

由于Snapshot的构造函数和方法是internal,因此程序集外部的调用者将其视为完全不透明且无法继承。程序集中的调用者可以调用Snapshot.Restore而不是MachineModel.Restore,但这不是一个大问题。此外,在实践中,如果没有访问Snapshot.Restore的私人成员,就永远无法实施MachineModel,这会阻止人们尝试这样做。

4 个答案:

答案 0 :(得分:3)

MachineModelSnapshot可以在同一个程序集中,而来自不同程序集的调用者吗?如果是这样,Snapshot可以是公共类,但具有完全内部成员。

答案 1 :(得分:3)

  

我显然可以做到这一点   向下转换,即拥有CurrentSnapshot   返回一个对象,并拥有   RestoreSnapshot接受一个对象   它回归到一个论点   快照。

问题是有人可以传递一个不是Snapshot的对象的实例。

如果您引入的接口 ISnapshot没有公开任何方法,并且只存在一个实现,那么您几乎可以以转发的价格确保类型安全。

我差点说,因为你不能完全阻止某人创建ISnapshot的另一个实现并传递它,这会破坏。但我觉得这应该提供所需的信息隐藏级别。

答案 2 :(得分:2)

您可以反转依赖关系并使Snapshot成为MachineModel的子(嵌套类)。然后,Snapshot只有一个公共(或内部)Restore()方法,该方法将MachineModel的实例作为参数。由于Snapshot被定义为MachineModel的子级,因此可以看到MachineModel的私有字段。

要恢复状态,下面的示例中有两个选项。您可以调用Snapshot.RestoreState(MachineModel)或MachineModel.Restore(Snapshot)*。

public class MachineModel
{
    public class Snapshot
    {
        int _mmPrivateField;

        public Snapshot(MachineModel mm) 
        { 
            // get mm's state
            _mmPrivateField = mm._privateField;
        }

        public void RestoreState(MachineModel mm) 
        { 
            // restore mm's state
            mm._privateField = _mmPrivateField;
        }
    }

    int _privateField;

    public Snapshot CurrentSnapshot
    {
        get { return new Snapshot(this); }
    }

    public void RestoreState(Snapshot ss)
    {
        ss.Restore(this);
    }
}

示例:

    MachineModel mm1 = new MachineModel();
    MachineModel.Snapshot ss = mm1.CurrentSnapshot;
    MachineModel mm2 = new MachineModel();
    mm2.RestoreState(ss);

*将Snapshot.RestoreState()作为internal并将所有调用者放在程序集之外是最简洁的,因此执行还原的唯一方法是通过MachineModel.RestoreState()。但是你在Jon的回答中提到在同一个程序集中会有调用者,所以没有多大意义。

答案 3 :(得分:0)

这是一个古老的问题,但是我一直在寻找非常相似的东西,所以我在这里结束,在这里所报告的信息与其他一些信息之间,我想出了这个解决方案,也许有点过大了,但是这样状态对象完全不透明,即使在装配级别也是如此

class Program
{
    static void Main(string[] args)
    {
        DoSomething l_Class = new DoSomething();

        Console.WriteLine("Seed: {0}", l_Class.Seed);

        Console.WriteLine("Saving State");

        DoSomething.SomeState l_State = l_Class.Save_State();

        l_Class.Regen_Seed();

        Console.WriteLine("Regenerated Seed: {0}", l_Class.Seed);

        Console.WriteLine("Restoring State");

        l_Class.Restore_State(l_State);

        Console.WriteLine("Restored Seed: {0}", l_Class.Seed);

        Console.ReadKey();
    }
}

class DoSomething
{
    static Func<DoSomething, SomeState> g_SomeState_Ctor;

    static DoSomething()
    {
        Type type = typeof(SomeState);
        System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(type.TypeHandle);
    }

    Random c_Rand = new Random();

    public DoSomething()
    {
        Seed = c_Rand.Next();
    }

    public SomeState Save_State()
    {
        return g_SomeState_Ctor(this);
    }

    public void Restore_State(SomeState f_State)
    {
        ((ISomeState)f_State).Restore_State(this);
    }

    public void Regen_Seed()
    {
        Seed = c_Rand.Next();
    }

    public int Seed { get; private set; }

    public class SomeState : ISomeState
    {
        static SomeState()
        {
            g_SomeState_Ctor = (DoSomething f_Source) => { return new SomeState(f_Source); };
        }

        private SomeState(DoSomething f_Source) { Seed = f_Source.Seed; }

        void ISomeState.Restore_State(DoSomething f_Source)
        {
            f_Source.Seed = Seed;
        }

        int Seed { get; set; }
    }

    private interface ISomeState
    {
        void Restore_State(DoSomething f_Source);
    }
}