3

This might be a stupid question, but I'm developing a component and I have a class with a property like the following

public class Grid()
{
 ..
 public IDecorator Decorator { get; set;}

}

What I want is for user to specify their own custom class that implements IDecorator in the following way

...BuildGrid(
grid=>{
..
grid.Decorator =  [CustomNameSpace].[DecoratorClass] 
//as in 
grid.Decorator =  Com.MyCompany.DivDecorator
..
});

Com.MyCompany.DivDecorator implements IDecorator interface. So how should I do it without the end user to specify the "new" keyword as in

grid.Decorator = new Com.MyCompany.DivDecorator();

I know I'm missing some key c# concept here.

Thanks

[Edit]

I was trying to do something like the Java DisplayTag library located here

http://www.displaytag.org/1.2/tut_decorators.html

The way they do it is by

... decorator="org.displaytag.sample.Wrapper"

So I guess instead of

public IDecorator Decorator { get; set;}

I should do

public String Decorator (get; set;}

and then internally use TypeOf() to resolve it.... Just wondering if there is any other way to elegantly do it in C#.

Thanks

Liming
  • 1,641
  • 3
  • 28
  • 38
  • 1
    Sounds like you want them to specify a type rather than an instance, then your internal grid code automatically news up an instance and uses it.. something like that maybe? Can you explain what the usage will be? – Charleh Jul 30 '13 at 15:16
  • So when you set Decorator equal to some class that implements IDecorator, you want to be able to access additional methods/properties that are not specified in the IDecorator interface? – Tyler Morrow Jul 30 '13 at 15:16
  • 1
    `grid.Decorator = Com.MyCompany.DivDecorator` is not valid syntax, AFAIK. Did you mean `grid.Decorator = typeof(...)`? – Tim S. Jul 30 '13 at 15:17
  • It sounds like what you are really after here is Dependency Injection. You could eliminate even passing the type by having it pre-registered for that particular interface. – James Jul 30 '13 at 15:22
  • Hey Guys: Thanks for the reply. Basically, I was looking at this Java component called DisplayTag located here http://www.displaytag.org/1.2/tut_decorators.html and I really like their way of doing it. Maybe like Charleh and Tim stated, I'm trying to specify a type. So in this case, I should specify a "String" that represents the class and then use typeOf() to get back the "interface" class? and RedLou, what I want is to access all the methods/property in the interface, but make it easy for end users like the Java DisplayTag. – Liming Jul 30 '13 at 15:23

5 Answers5

8

Well you can use a factory method:

public class DivDecorator
{
    public static IDecorator Create()
    {
        return new Com.MyCompany.DivDecorator()
    }
}

...

grid.Decorator = Com.MyCompany.DivDecorator.Create();

Or a singleton:

public class DivDecorator
{
    public static readonly IDecorator Instance = new DivDecorator();
}

...

grid.Decorator = Com.MyCompany.DivDecorator.Instance;

On both these cases, you're just moving the new operator to a different location.

Alternatively, you could have the user specify the type and then you'll have to worry about instantiating it when you need to:

public class Grid
{
    public Type DecoratorType { get; set; }

    private IDecorator CreateDecorator()
    {
        return (IDecorator)Activator.CreateInstance(this.DecoratorType);
    }
}

...

grid.Decorator = typeof(Com.MyCompany.DivDecorator);

Or even better, use generics:

public class Grid<T> where T : IDecorator, new
{
    private T CreateDecorator()
    {
        return new T();
    }
}

BuildGrid<Com.MyCompany.DivDecorator>(grid => ... );
p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
  • Thanks p.s.w.g. I think I like your .CreateInstance approach the best. Though, not sure if use reflection is a good thing or not, but it seems to be the cleanest way. GREATLY appreciate your effort showing me the different approach to solve this ! I like the Generic approach as well, though, I have different decorators throughout code, IDecorator, IRowDecorator, IColumnDecorator etc.. – Liming Jul 30 '13 at 15:55
  • Use the generic version over reflection. They both, as written, require a parameterless constructor, but the generic version will catch it at compile time if one does not exist. – lukegravitt Jul 30 '13 at 17:39
  • @Liming There are pros and cons to each method. I prefer a generic method, but with the generic class you'd have to specify each decorator type as a different generic parameter. There are some other tricks you could use, such a having a generic method like `UseDecorator()`. – p.s.w.g Jul 30 '13 at 18:01
  • @p.s.w.g. After thinking about it. I think I start to like the Generic Method much better. I'm assuming end user of my component will be able to do like so grid.UseDecorator() and then, the signature looks like IDcorator UseDecorator(){ return new T();}, right? – Liming Jul 30 '13 at 19:44
  • 1
    @Liming Yeah precisely. If you did something like `Grid UseDecorator(){ this.decorator = new T(); return this; }`, you could create a kind of [fluent API](http://en.wikipedia.org/wiki/Fluent_interface). e.g. something like `BuildGrid(grid => grid.UseDecorator().UseRowDecorator().WithPaging(pageSize)...)` – p.s.w.g Jul 30 '13 at 19:52
  • p.s.w.g, just saw this message. absolutely brilliant! I will implement as such! – Liming Jul 31 '13 at 04:38
5

If I understand what you're looking for, you can use a generic:

public class Grid<TDecorator> where TDecorator : IDecorator, new() {

    private readonly TDecorator _decorator;

    public Grid() {
        _decorator = new TDecorator();
    }

    public TDecorator Decorator { get { return _decorator; } }
}

They'd get a grid by calling:

var myGrid = new Grid<MyDecorator>();
Joe Enos
  • 39,478
  • 11
  • 80
  • 136
  • Thanks Joe. I like the Generics approach as well. I will definitely look into Generics as well. Greatly appreciate it. – Liming Jul 30 '13 at 15:58
3

You could make BuildGrid generic, and specify a new() constraint.

e.g. BuildGrid<T>() where T : IDecorator, new()

Though I'm not 100% clear on what you're trying to go for.

Mark Sowul
  • 10,244
  • 1
  • 45
  • 51
  • Thanks Mark. I like the Generics approach as well. Seem like multiple of you suggested this approach. I will definitely look into Generics. – Liming Jul 30 '13 at 15:59
1

Given you have an interface, you will be be creating an instance somewhere. You can inject it (see IOC patterns, or an Instance factory pattern)

There are various scenarios. but understanding factory patterns will help

1) The class is inside the assembly. discover the type/s Getting all types that implement an interface and create an instance http://msdn.microsoft.com/en-us/library/wccyzw83.aspx

2) the code is external, http://msdn.microsoft.com/en-us/library/dd460648.aspx

3) Or a file http://msdn.microsoft.com/en-us/library/system.reflection.assembly.load%28v=vs.110%29.aspx

Community
  • 1
  • 1
phil soady
  • 11,043
  • 5
  • 50
  • 95
0

Thanks to everyone who helped me out! I decided to go with p.s.w.g's Activator.CreateInstance method. The reason is, for the Grid Component, I have a table level Decorator and then each column for the grid, can have their own decorator as well. As in

@Model.[MyIEnumberable].BuildGrid<Product>(grid=>{
grid.ShowHeader = false;
grid.CellSpacing = 0;
grid.CellPadding = 0;
grid.Decorator = "Com.MyCompany.DivDecorator";
grid.columns(
        grid.column(HeaderText: "First Column", Decorator:"Com.MyCompany.FirstColumnDecorator"...),
        grid.column(HeaderText: "Second Column", Decorator:"Com.MyCompany.SecondColumnDecorator"..),
        grid.column(HeaderText: "ThirdColumn", Text:@item.ProductID),
        grid.column(HeaderText: "ThirdColumn", Text:@item.[A Property only exist in grid.Decorator, achieved using dynamic/DynamicObject])
)
})

So given my situation and since each column can possibility have its own decorator class.So I will have to use Activator.CreateInstance for them.

Thank you all once again for your help!! Greatly appreciate your time once again!!

[Edit]. After reading p.s.w.g's last two comments, generics method is the way to go!

Liming
  • 1,641
  • 3
  • 28
  • 38