我正在用C#编写一个支持插件的应用程序。每个插件都必须自我介绍,以便应用程序可以为它准备适当的环境。当前的info对象看起来更像是这样:
class FilterInfo
{
public InputInfo[] inputs;
public OutputInfo[] outputs;
bool IsConfigurable;
bool IsPlayable;
string TypeName;
}
这种结构将来肯定会扩大(不过,我想,这并不多,它可能会增加一倍)。我正在考虑如何正确实现这样的信息类。
在C ++中,我会按照以下方式进行操作(我会将类剥离到一个字段以使示例更具可读性):
class FilterInfo
{
private:
std::vector<const InputInfo> inputs;
public:
std::vector<const InputInfo> & GetInputs()
{
return inputs;
}
const std::vector<const InputInfo> & GetInputs() const
{
return inputs;
}
}
现在,插件会实例化一个FilterInfo
类,填写其字段,然后根据请求返回const FilterInfo
,这样就没有人可以更改信息的内容(好吧,没有人应该)。< / p>
在C#中,我只能想象以下“安全”解决方案:
public interface IInputInfo
{
bool SomeData
{
get;
}
}
public class InputInfo : IInputInfo
{
private bool someData;
public bool SomeData
{
get
{
return someData;
}
set
{
someData = value;
}
}
public bool IInputInfo.SomeData
{
get
{
return someData;
}
}
}
public interface IFilterInfo
{
ReadOnlyCollection<IInputInfo> Inputs
{
get;
}
}
public class FilterInfo : IFilterInfo
{
private InputInfo[] inputs;
public InputInfo[] Inputs
{
get
{
return inputs;
}
set
{
inputs = value;
}
}
public ReadOnlyCollection<IInputInfo> IFilterInfo.Inputs
{
return inputs;
}
}
插件当然会返回IFilterInfo
而不是FilterInfo
,这样数据就是只读(好吧,我知道反射,问题是通知用户,数据< em>不应该被改变)。然而,这个解决方案对我来说看起来非常笨拙 - 特别是与之前引用的紧凑版本相比。
另一个解决方案可能只是使用getter创建FilterInfo,但它需要以某种方式将数据传递给它,并且可能最终会得到一个包含大量参数的巨大构造函数。
编辑:另一种解决方案是创建一个结构并在每个请求期间返回其副本。但是,数组是通过引用复制的,因此我每次都必须手动复制它们。
另一个是每次有人请求时从头开始构造FilterInfo,例如
public FilterInfo Info
{
get
{
return new FilterInfo()
{
IsConfigurable = true,
IsPlayable = false,
Inputs = new[]
{
new InputInfo()
{
// (...)
}
}
}
}
}
有没有一种优雅的方法来解决这个问题?
答案 0 :(得分:1)
我认为你第一次几乎是这样:
IFilterInfo
界面。FilterInfo
类中实现接口,该接口在其属性上具有内部setter。FilterInfo
类的新实例。公约建议在每次构建新实例的情况下使用方法而不是属性。 (如果您坚持使用属性,则可以在构建实例后将其存储并通过属性返回)示例:强>
在可插拔组件中:
public interface IFilterInfo {
bool IsPlayable { get; }
bool IsConfigurable { get; }
}
在插件程序集中:
internal class FilterInfo : IFilterInfo {
public bool IsPlayable { get; internal set; }
public bool IsConfigurable { get; internal set; }
}
public IFilterInfo GetFilterInfo() {
return new FilterInfo() { IsPlayable = true, IsConfigurable = false };
}
内部setter和只读接口应该足以确保在插件程序集之外不修改属性。
答案 1 :(得分:0)
如何将setter设置为private或protected。
public class FilterInfo
{
public InputInfo[] inputs { get; private set; }
public OutputInfo[] outputs { get; private set; };
bool IsConfigurable;
bool IsPlayable;
string TypeName;
public void SetInputs(...)
{
InputInfo[] allInputs;
//do stuff
inputs = AllInput;
}
public void SetOutputs(...)
{
OutputInfo[] allOutputs;
//do stuff
outputs = AllOutput;
}
}
您可以使用内部方法设置数据或受保护,并允许通过继承修改对象。
<强>更新强> 如何使用setter的内部访问器。这样,除非在InternalsVisibleTo程序集级别属性中声明,否则任何东西都无法访问setter,该属性将在包含FilterInfo的程序集中定义。
以下帖子详细说明了如何使用internal关键字执行此操作。 Internal Description
<强>更新强>
另一个解决方案可能只是使用getter创建FilterInfo,但它需要以某种方式将数据传递给它,并且可能最终会得到一个包含大量参数的巨大构造函数。
根据这一点,没有吸气剂的唯一问题是你仍然需要传递数据。原始解决方案允许这种情况发生。我想我可能会有点困惑。如果插件能够更改此API中的信息,我猜是通过引用。然后,如果应用程序引用相同的程序集,它也将为插件提供相同的访问器。似乎没有将setter设置为内部并允许通过属性访问是实现该类型功能的唯一方法。但是这不适用于您的情况,因为您不知道引用API的程序集。
答案 2 :(得分:0)
我不太确定你真正想要的是什么,但似乎构建模式对这种情况有好处。
首先,setter或构造函数可以标记为internal,意味着只有程序集才能访问构造函数或setter。让getter公开,这是必要的,不是吗?
然后你的构建器类(假设你正在使用构造函数注入):
public class FilterInfoBuilder{
public FilterInfoBuilder(InputInfo[] inputInfo){
this.inputInfo = inputInfo;
}
private InputInfo[] inputInfo;
public FilterInfo Create(){
FilterInfo filterInfo = new FilterInfo(inputInfo);
return filterInfo;
}
}
也许我误解了你的要求。
修改强>
您可以将构建器调整为动态setter。现在考虑使用内部setter而不是内部构造函数。
public class FilterInfoBuilder{
public FilterInfoBuilder(InputInfo[] inputInfo){
filterInfo = new FilterInfo();
filterInfo.InputInfo = inputInfo;
}
private FilterInfo filterInfo;
public FilterInfo FilterInfo{
get{
return filterInfo;
}
}
public void ChangeInputInfo(InputInfo[] inputInfo){
filterInfo.InputInfo = inputInfo;
}
}
您可以使用FilterInfoBuilder.FilterInfo
访问FilterInfo类。要修改它,可以在构建器类中创建内部方法。
我不太确定解决方案,因为我没有在任何文档资料中找到设计。
更多编辑
我有另一种设计,只有你可以分开程序集之间的接口并确保应用程序访问接口而不是类。
示例:
public interface IInputInfoSetable{
public InputInfo[] InputInfo{
set;
}
}
public interface IFilterInfo{
public InputInfo[] InputInfo{
get;
}
}
public class FilterInfo: IFilterInfo, IInputInfoSetable{
// implement explicitly both of the interface.
}