我有一个应该返回当前状态快照的方法,以及另一个恢复该状态的方法。
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
,这会阻止人们尝试这样做。
答案 0 :(得分:3)
MachineModel
和Snapshot
可以在同一个程序集中,而来自不同程序集的调用者吗?如果是这样,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);
}
}