Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I use Ninject ConstructorArguments with strong naming?

Well, I don't know if "strong naming" is the right term, but what I want to do is as follows.

Currently I use ConstructorArgument like e.g. this:

public class Ninja
{
    private readonly IWeapon _weapon;
    private readonly string _name;

    public Ninja(string name, IWeapon weapon)
    {
        _weapon = weapon;
        _name = name;
    }
    // ..more code..
}

public void SomeFunction()
{
    var kernel = new StandardKernel();
    kernel.Bind<IWeapon>().To<Sword>();
    var ninja = kernel.Get<Ninja>(new ConstructorArgument("name", "Lee"));
}

Now, if I rename the parameter "name" (e.g. using ReSharper) the ConstructorArgument won't update, and I will get a runtime error when creating the Ninja. To fix this I need to manually find all places I specify this parameter through a ConstructorArgument and update it. No good, and I'm doomed to fail at some point even though I have good test coverage. Renaming should be a cheap operation.

Is there any way I can make a reference to the parameter instead - such that it is updated when I rename the parameter?

like image 571
stiank81 Avatar asked Apr 14 '10 11:04

stiank81


2 Answers

If you can share more of what you're really trying to achieve, you'll get a better answer. In general, you dont want to be relying on passing a ConstructorArgument at all if that can be helped - it should be a last resort way of shoehorning a parameter value into the creation of a component you don't own and hence can rely on not being renamed [as] willy nilly during refactoring activities. So for normal code, if you can try to keep it to interfaces to make things unambiguous and not be relying on the names that's better.

Cant dig up an example right now but there's a pretty common idiom called static reflection. A supplied ConstructorArgument can match any parameter of that name across any of the constructors so static reflection isnt the most appropriate thing in this instance.

Hence the best that'll static reflection will probably allow you to achieve is something like:

var ninja = ninject.Get<Ninja>( ParamNamesOf(()=>new Ninja( "dummy", "dummy" )).First() );

The typical example you'll see is where one wants to extract the name of a property being accessed on an instance. This is a little different as it needs to work on a constructor invocation expression.

As for finding an appropriate lib that already has just that, exercise for the searcher :D (But I'd suggest finding a better way to express what you want to do that doesnt use ConstructorArgument in preference to this approach anyway.)

like image 125
Ruben Bartelink Avatar answered Oct 29 '22 04:10

Ruben Bartelink


As you already noticed, this approach is very fragile and should be avoided. Try a design were you don't need to add the name as a constructor argument. You could do this for instance:

public class Ninja
{
    private readonly IWeapon _weapon;

    public Ninja(IWeapon weapon)
    {
        _weapon = weapon;
    }

    public string Name { get; set; }

    // ..more code..
}

public void SomeFunction()
{
    var kernel = new StandardKernel();
    kernel.Bind<IWeapon>().To<Sword>();
    var ninja = ninject.Get<Ninja>();
    ninja.Name = "Lee";
}

I hope this helps.

like image 40
Steven Avatar answered Oct 29 '22 02:10

Steven