2

I'm writing an application in C#, which supports plugins. Each plugin has to introduce itself, such that application can prepare appropriate environment for it. The current info object looks more less like this:

class FilterInfo
{
    public InputInfo[] inputs; 
    public OutputInfo[] outputs;
    bool IsConfigurable;
    bool IsPlayable;
    string TypeName;
}

This structure will surely expand in future (however, I guess, that not much, it'll maybe double its size). I'm currently thinking on how to implement such info class properly.

In C++ I would do it the following way (I'll strip the class to one field to make the examples more readable):

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

Now, the plugin would instantiate a FilterInfo class, fill-in its fields and then return const FilterInfo on request, such that noone may change contents of the info (well, noone should).

In C#, I can only imagine the following "safe" solution:

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

The plugin will, of course, return IFilterInfo instead of FilterInfo, such that the data is readonly (OK, I know about reflection, the matter is to notify the user, that the data should not be changed). However, this solution looks very clumsy to me - especially when compared to compact version I cited earlier.

Another solution may to be create FilterInfo only with getters, but it would require passing the data into it in some way and probably would end up with a huge constructor with lots of parameters.


Edit: Another solution is to create a struct and return its copy during every request. However, arrays are copied by reference, so I would have to copy them manually each time.

Yet another one is to construct the FilterInfo from the scratch each time anyone requests it, eg.

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

Is there an elegant way to solve this problem?

Spook
  • 25,318
  • 18
  • 90
  • 167

3 Answers3

1

I think you got it almost right the first time:

  1. Define a public IFilterInfo interface in the pluggable assembly that only allows reading.
  2. Implement the interface in a FilterInfo class in the plugin assembly that has internal setters on its properties.
  3. Have a method return a new instance of the FilterInfo class upon request. Convention suggests to use a method instead of a property in cases where a new instance is constructed each time. (If you insist on using a property you could store the instance once it has been constructed and return it through the property)

Example:

In the pluggable assembly:

public interface IFilterInfo {
  bool IsPlayable { get; }
  bool IsConfigurable { get; }
}

In the plugin assembly:

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

Internal setters and a read-only interface should be enough to ensure that the properties aren't modified outside the plugin assembly.

bernhof
  • 6,219
  • 2
  • 45
  • 71
  • That won't work in my case. The plugin requires specific environment when it is constructed and I prepare this environment basing on FilterInfo I get from the plugin's factory class. – Spook Apr 09 '13 at 06:25
  • Ok, in that case I think you're close. See the updated answer. – bernhof Apr 09 '13 at 07:14
  • I guess, that this solution will suit my needs best. – Spook Apr 09 '13 at 08:50
0

What about setting the setters to private or 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;
    }
}

You would be able to have internal methods to set the data or go protected and allow modifying the objects through inheritance.

UPDATE What about using the internal accessor for the setter. This way nothing will be able to access the setter unless it is declared in the InternalsVisibleTo assembly level attribute, which would be defined in the assembly containing FilterInfo.

The following post gives a good explanation on how to do this using the internal keyword. Internal Description

UPDATE

Another solution may to be create FilterInfo only with getters, but it would require passing the data into it in some way and probably would end up with a huge constructor with lots of parameters.

According to this the only issue with not having a getter is that you still need to pass in data. The original solution allows this to happen. I guess I might be a little confused. If the plugin is able to change the information in this API which is by reference I am guessing. Then if the application is referencing the same assembly, it too would have the same accessors provided to the plugin. It seems that short of setting the setters to internal and allowing access through attributes would be the only way to achieve that type of functionality. But that wont work in your case because you do not know the assemblies that are referencing your API.

Community
  • 1
  • 1
DSlagle
  • 1,563
  • 12
  • 19
  • That won't work, I'm affraid. The plugin class has to be able to fill-in the public properties of a FilterInfo, but the application should not be able to. Also, FilterInfo is held in a separate assembly used by both application and plugin, so making the setters internal is not an option too. – Spook Apr 09 '13 at 05:52
  • Plugins are loaded dynamically, I have no knowledge of them during compilation of the project, so I cannot set the InternalsVisibleTo attribute of the API assembly... – Spook Apr 09 '13 at 06:05
0

I don't quite sure about what you really want, but it seems the builder pattern is good for this case.

First, the setter or constructor can be marked internal, means that only the assembly can access the constructor or setter. Leave the getter public, it is needed, isn't it?

Then your builder class (assume you are using the constructor injection):

public class FilterInfoBuilder{
  public FilterInfoBuilder(InputInfo[] inputInfo){
    this.inputInfo = inputInfo;
  }

  private InputInfo[] inputInfo;
  public FilterInfo Create(){
    FilterInfo filterInfo = new FilterInfo(inputInfo);
    return filterInfo;
  }
}

Maybe I misunderstand your requirement though.

EDIT

You can tweak the builder as a dynamic setter though. Now consider using internal setter instead of internal constructor.

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

You can use FilterInfoBuilder.FilterInfo to access the FilterInfo class. To modify it, you can create internal methods inside the builder class.

I don't really sure about the solution though, as I haven't found the design in any documented source.

More EDIT

I have another design, only if you can separate the interface between assemblies and make sure the application access the interface and not the class.

example:

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.
}
Fendy
  • 4,565
  • 1
  • 20
  • 25
  • This is a variation on the solution with building FilterInfo each time (the one I added to the post). In fact, a little more elegant than mine, but still will require building the info each time anyone requests it. – Spook Apr 09 '13 at 06:21
  • Maybe I still missing your real requirement or what you really try to achieve. Maybe more usage case scenario can help – Fendy Apr 09 '13 at 06:31