G. Manucci
G. Manucci

Reputation: 31

C# Constructor Pattern based on class methods

I need to create a system which takes into account the current assembly and perform x action based on that (reflection).

The main idea is to assign attributes to methods, so it doesnt require lot of maintenance for the next years, but also new classes will be developed to similar situation with new attributes/methods.

The main problem is that I have different arguments for different classes (i'm inheriting from a abstract class)

Base class:

public abstract class HelpItem : INotifyPropertyChanged
{
    public HelpItem()
    {

    }

    string group;
    SvgImage image;
    string description;
    string help;
    string name;

    public string Name
    {
        get { return name; }
        set
        {
            name = value;
        }
    }


    public string Help
    {
        get { return help; }
        set
        {
            if (help == value)
                return;
            help = value;
            OnPropertyChanged(this, new PropertyChangedEventArgs(nameof(Help)));
        }
    }


    public string Group
    {
        get { return group; }
        set
        {
            if (group == value)
                return;
            group = value;
            OnPropertyChanged(this, new PropertyChangedEventArgs(nameof(Group)));
        }
    }


    public string Description
    {
        get { return description; }
        set
        {
            if (description == value)
                return;
            description = value;
            OnPropertyChanged(this, new PropertyChangedEventArgs(nameof(Description)));
        }
    }


    public SvgImage Image
    {
        get { return image; }
        set
        {
            if (image == value)
                return;
            image = value;
            OnPropertyChanged(this, new PropertyChangedEventArgs(nameof(Image)));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        PropertyChanged?.Invoke(sender, e);
    }

    [Action("&Open")]
    public abstract void Run();

    public static IList<HelpAttribute> GetHelpItems()
    {
        return GetHelpItems<HelpItem>();
    }


}

An use of this class will be:

    public class PfdHelpItem : HelpItem
{
    public PfdHelpItem(System.IO.MemoryStream stream, DevExpress.XtraBars.Docking2010.DocumentManager docManager)
    {
        DocManager = docManager;
        Stream = stream;
    }

    public MemoryStream Stream { get; set; }
    public DocumentManager DocManager { get; set; }

    bool isExternal;

    public bool IsExternal
    {
        get { return isExternal; }
        set
        {
            if (isExternal == value)
                return;
            isExternal = value;
            OnPropertyChanged(this, new PropertyChangedEventArgs(nameof(IsExternal)));
        }
    }

    public override void Run()
    {
        ...
    }

    [Action("Open &externally")]
    public void RunExternal()
    {
        ...
    }

    [Action("&Save as...")]
    public void SaveAs()
    {
       ...
    }
}

Attribute class:

public abstract class HelpAttribute : Attribute
{
    public HelpAttribute(string name, string title, string description)
    {
        Description = description;
        Title = title;
        Name = name;
    }

    public string Name { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
}

Then I have other child classes:

public class PdfHelpAttribute : HelpAttribute
{
    public PdfHelpAttribute(string name, string title, string description) 
        : base(name, title, description)
    {

    }
}

Then the use of this attribute:

    /// <summary>
    ///   Looks up a localized resource of type System.Byte[].
    /// </summary>
    [Misc.Help.PdfHelp("MyFileName", "Title", "Desc")]
    internal static byte[] MyPdfFile{
        get {
            object obj = ResourceManager.GetObject("MyPdfFile", resourceCulture);
            return ((byte[])(obj));
        }
    }

So as the sample shows, in this case it will get PDF files from resource and deal with it, open, save, etc.

There's also OnlineHelp class, which is filled with url addresses and has other actions.

The Idea is to get all the HelpItems from reflection and show as needed.

Is there a known pattern to work with that situation?

Thanks in advance!

Upvotes: 0

Views: 503

Answers (1)

Peter Waher
Peter Waher

Reputation: 188

One pattern that you can use is the following:

  1. Define an interface for the desired functionality you want to find.
  2. Implement the interface in the abstract base class.
  3. Create an inventory class.
  4. Initialize the inventory class with a set of assemblies. This set can be staticly defined, or dynamicly defined by looping through available assemblies in the application domain.
  5. Loop through all types in the assemblies.
  6. Loop through all interfaces in these types (and base types)
  7. Build an inventory of what types are available implementing different interfaces, using an appropriate structure.
  8. Add a method to get available types during runtime when needed.

The reason for using interfaces as the common denominator, is that they are typically much fewer than all the types available, making the creation of the inventory quicker and consume less memory.

Example: Look at the Initialize and GetTypesImplementingInterface methods in the static Types class of the Waher.Runtime.Inventory nuget:

https://github.com/PeterWaher/IoTGateway/blob/master/Runtime/Waher.Runtime.Inventory/Types.cs

It can be combined with the TypesLoader class to dynamically load all available assemblies during startup:

https://github.com/PeterWaher/IoTGateway/blob/master/Runtime/Waher.Runtime.Inventory.Loader/TypesLoader.cs

Upvotes: 1

Related Questions