2

我正在用 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根据请求返回,这样没有人可以更改信息的内容(好吧,没有人应该)。

在 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,这样数据是只读的(好的,我知道反射,问题是通知用户,数据不应该被更改)。然而,这个解决方案对我来说看起来很笨拙——尤其是与我之前引用的紧凑版本相比。

另一种解决方案可能是仅使用 getter 创建 FilterInfo,但它需要以某种方式将数据传递给它,并且可能最终会产生一个带有大量参数的巨大构造函数。


编辑:另一个解决方案是创建一个结构并在每个请求期间返回其副本。但是,数组是通过引用复制的,所以我每次都必须手动复制它们。

还有一个是每次有人请求它时从头开始构建FilterInfo,例如。

public FilterInfo Info
{
    get
    {
        return new FilterInfo()
            {
                IsConfigurable = true,          
                IsPlayable = false,
                Inputs = new[]
                    {
                        new InputInfo()
                            {
                            // (...)
                            }
                    }
            }
    }
}

有没有优雅的方法来解决这个问题?

4

3 回答 3

1

我认为您第一次几乎是正确的:

  1. 在可插入程序集中定义一个只允许读取的公共IFilterInfo接口。
  2. 在插件程序FilterInfo集中的类中实现接口,该类的属性具有内部设置器。
  3. 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 };
}

内部设置器和只读接口应该足以确保不会在插件程序集之外修改属性。

于 2013-04-09T06:19:13.250 回答
0

如何将设置器设置为私有或受保护。

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;
    }
}

您将能够拥有内部方法来设置数据或受到保护并允许通过继承修改对象。

更新 如何使用设置器的内部访问器。这样,除非在 InternalsVisibleTo 程序集级别属性中声明,否则任何东西都无法访问 setter,该属性将在包含 FilterInfo 的程序集中定义。

下面的帖子很好地解释了如何使用 internal 关键字来做到这一点。 内部描述

更新

另一种解决方案可能是仅使用 getter 创建 FilterInfo,但它需要以某种方式将数据传递给它,并且可能最终会产生一个带有大量参数的巨大构造函数。

据此,没有吸气剂的唯一问题是您仍然需要传入数据。原始解决方案允许这种情况发生。我想我可能有点困惑。如果插件能够更改此 API 中的信息,我猜这是通过引用。然后,如果应用程序引用相同的程序集,它也将具有提供给插件的相同访问器。似乎没有将设置器设置为内部并允许通过属性访问将是实现该类型功能的唯一方法。但这不适用于您的情况,因为您不知道引用您的 API 的程序集。

于 2013-04-09T05:49:27.533 回答
0

我不太确定您真正想要什么,但是构建器模式似乎适合这种情况。

首先,setter或constructor可以标记为internal,意味着只有程序集才能访问constructor或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;
  }
}

也许我误解了你的要求。

编辑

不过,您可以将构建器调整为动态设置器。现在考虑使用内部设置器而不是内部构造器。

  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.FilterInfoFilterInfo 类来访问。要修改它,您可以在构建器类中创建内部方法。

不过,我不太确定解决方案,因为我没有在任何文档来源中找到该设计。

更多编辑

我有另一种设计,前提是您可以分离程序集之间的接口并确保应用程序访问接口而不是类。

例子:

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.
}
于 2013-04-09T06:12:18.973 回答