Autofac: how to inject properties with dynamic values? - c#

By Autofac, it's easy to inject a static value to the CurrentDate property to instances of classes in a given assembly:
builder.RegisterApiControllers(asm).WithProperty("CurrentDate", new DateTime(2012, 1, 13));
However, how to inject dynamic values e.g. values returned by a lamda () => { return DateTime.Now; } to the CurrentDate property?

Sounds like you could use pretty standard property injection, like this:
builder.RegisterApiControllers(asm)
.OnActivating(e => { e.Instance.CurrentDate = DateTime.Now; });
Note that you may need to cast e.Instance as it will probably be of type Object.
See Lifetime Events in the documentation for more info.
On second thought, why not just put the initialization in the base class constructor?
public DateTime CurrentDate { get; private set; }
protected ApiController() { CurrentDate = DateTime.Now; }
The current date isn't really a dependency you need a DI container to provide.

Register another service that provides your dynamic values (e.g. IDateTimeService) [I assume it's really something more complex than a DateTime that you want.] The default lifetime for this new service will be Instance per dependency but you could use "Per Matching Lifetime Scope". Your Controllers will already be created per Http request.
Now simply add a dependency from your controllers on the IDateTimeService (in the constructor). In the methods within that controller you can now get hold of the dynamic value you want from that service.
private static readonly IDateTimeService datetimeService;
public MyController (IDateTimeService datetimeService)
{
this.datetimeService = datetimeService;
}
public void SomeMethod()
{
var date = datetimeService.GetDate();
...
}

You need to write custom parameter like this:
public class DelegateParameter : Parameter
{
private readonly string _name;
private readonly Func<object> _getValue;
public DelegateParameter(string name, Func<object> getValue)
{
if (name == null) throw new ArgumentNullException("name");
if (getValue == null) throw new ArgumentNullException("getValue");
_name = name;
_getValue = getValue;
}
public override bool CanSupplyValue(ParameterInfo pi, IComponentContext context, out Func<object> valueProvider)
{
PropertyInfo propertyInfo = GetProperty(pi);
if (propertyInfo == null || propertyInfo.Name != _name)
{
valueProvider = null;
return false;
}
valueProvider = _getValue;
return true;
}
private static PropertyInfo GetProperty(ParameterInfo pi)
{
var methodInfo = pi.Member as MethodInfo;
if (methodInfo != null && methodInfo.IsSpecialName && (methodInfo.Name.StartsWith("set_", StringComparison.Ordinal) && methodInfo.DeclaringType != null))
return methodInfo.DeclaringType.GetProperty(methodInfo.Name.Substring(4));
return null;
}
}
And then use it:
builder.RegisterApiControllers(asm).WithProperty(new DelegateParameter("CurrentDate", () => DateTime.Now));

If you are trying to inject the lambda expression, rather than the result of the lambda expression, you have quite a few imperfect options. Here are just a few; I'm sure that there are more.
Autofac
The Autofac wiki on Google Project Hosting documents four ways of injecting properties. Three of them appear to use constant or default values--you mentioned one of these methods.
The final seems to give the developer a bit more control over properties. It uses the OnActivating event, during which you have a few options. You could:
Set the property and hope it sticks.
If the property lacks an accessible setter, you could use reflection to set it, or its backing property (by default, m_PropertyName for a property named PropertyName, if I recall correctly).
Wrap the instance in a proxy, as they put it: see Polymorphism below.
Polymorphism
Let ClassA contain the property to be modified, Prop1. Create a new class ClassB that extends ClassA. If Prop1 has a virtual modifier, you can override it. Otherwise, use the new modifier to create a similar property in ClassB containing your dynamic code.
In the case of an override, you will need to instantiate ClassB in place of ClassA. This will not work if the framework creates its own instances of ClassA, but as long as you create your own instances of ClassB and pass them to the framework, you should be good to go.
If you are using a new property, in addition to instantiating ClassB, you also have to ensure that whenever you access the new property, the object is cast to ClassB or a descendant type. This generally will not work if another framework is designed to use ClassA, since it will always operate on type ClassA, not ClassB, regardless of your casting.
Bytecode Manipulation
This is nasty stuff, but it will do exactly what you want. C# generally compiles to an assembly/bytecode language called CIL. Microsoft's variant is MSIL, but it's pretty much identical to generic CIL.
I've always used Mono.Cecil for CLI/CLR (.NET, Mono) bytecode manipulation. It seems to work flawlessly, and it's quite nice once you get the hang of it. However, you have to know two things:
How to use CIL
How to use Mono.Cecil
The first one isn't that bad. A few Wikipedia pages with detailed tables are all that you need, provided that you have sufficient experience with CLI. If you think CLI stands for nothing other than "command line interface", then you might run into difficulty.
Mono.Cecil, on the other hand, lacked any form of proper documentation as of about a year ago (2012). The learning curve was impossibly steep. I had a miserable few days trying to figure it out. It's amazing when it works, though.

Related

Factory Pattern, selecting by Property

I have a (growing) list of Data-Generators. The generator that I need is created by a factory class. The generators all implement a common Interface, which includes among other things a static string name.
What I would like to do: Call the factory.Create method with a string parameter for the above mentioned name. The create method finds the generator with this name and returns a new instance of said generator.
Bonus in my opinion of this way to do it: I only have to add new generator classes without having to edit the factory.
Question:
Is this a good way to handle this problem?
How can I find all generators? Reflection over every implementation of the interface/every member of the namespace (unique for the generators + their interface)?
Is it correct to call this way of working a factory, or is this some different pattern?
In the end I would call the factory like this (simplified):
//Caller
public DataModel GetData2()
{
var generator = new DataFactory().Create("Gen.2");
return generator.GetData();
}
//Factory
public class DataFactory
{
public AbstractDataGenerator Create(string type)
{
//Here the magic happens to find all implementations of IDataGenerator
var allGenerators = GetImplementations();
var generator = allGenerators.FirstOrDefault(f => f.name == type);
if (generator != null)
return (AbstractDataGenerator)Activator.CreateInstance(generator);
else
return null;
}
}
//Interface
public abstract class AbstractDataGenerator
{
public static string name;
public abstract DataModel GetData();
}
//Data-Generators
public class DataGen1 : AbstractDataGenerator
{
public static string name = "Gen.1";
public DataModel GetData()
{
return new DataModel("1");
}
}
public class DataGen2 : AbstractDataGenerator
{
public static string name = "Gen.2";
public DataModel GetData()
{
return new DataModel("2");
}
}
Should the magic GetImplementations() in the factory be done via Reflection or somehow different? Should I use a completely different approach?
Since answers refer to IoC and DI: This project uses NInject already, so it would be available.
Switched from interface to abstract class.
Is this a good way to handle this problem?
Having a factory to get an instance of the logic class you need by some key - I believe it is a good way. It is a pattern that I use a lot myself. About the way you have your key - I'd prefer to not have it as a static member (regardless to the fact that interfaces can't have static members) but just as a property and to add a base class to the IDataGenerator. That base class will have a constructor that will get the name - That way each new DataGenerator you create will have to set it and you wont forget.
About having the name as a string - I personally prefer having it "strongly typed". What I mean is that if I pass Gen . 2 instead of Gen.2 with strings I will discover this problem only in runtime. Possible other ways (if you want, because a simple string is fine too - a matter of taste):
Replace strings with an enum
Have a static class with static readonly strings for all your values - then in your code use those values. You get the benifits of the intellisense and of not getting the string wrong but better than enum - you can just still pass strings that are not in the "list" so you can add new ones as add-ons.
Have a RequestGenerator object, with each Generator being IDataGenerator<TGeneratorRequest>. This might be an overkill but if you have also extra information you need for the creating of a DataGenerator which differs between them then consider it .
How can I find all generators? Reflection over every implementation of the interface/every member of the namespace (unique for the generators + their interface)?
Yes, reflection can be a good way to do so. However, I would suggest to read into Dependency Injection and IoC Containers like Castle Windsor for example. There are things out there that already implement it for you, so why to re-invent the wheel :)
DI is a life changer concept in my opinion
Is it correct to call this way of working a factory, or is this some different pattern?
Yap. It is a Factory
Should the magic GetImplementations() in the factory be done via Reflection or somehow different?
See answer for question 2
This is where constructor injection can REALLY shine. Look into dependency injection tools and employ one! It also checks your "Bonus" request.
Here's what your factory might look like with constructor injection:
public class DataFactory
{
private Dictionary<string, IDataGenerator> generators;
public DataFactory(IDataGenerator[] generatorReferences)
{
this.generators = generatorReferences
.ToDictionary(k => k.name, v => v);
}
public IDataGenerator Create(string type)
{
IDataGenerator generator = null;
this.generators.TryGetValue(type, out generator);
return generator;
}
}
Most DI software has the capability to automatically scan assemblies for implementations of a certain type (e.g. IDataGenerator) and register those with itself, when it constructs an instance of your DataFactory it'll automatically include them.

How to use a lambda to specify a constructor argument?

I am not overly familiar with implementing lambdas and expressions, but I've used to this syntax many times in MVC where the lambda is identifying a property on an object:
Html.Label(model => model.Foo)
In my app I am using Ninject conditional bindings to supply the instance of the Settings class which is injected when I request an instance of Class. My Class looks like this:
public class Class
{
private readonly Settings settings;
public Settings Settings { get { return settings; } }
public Class(Settings settings)
{
this.settings = settings;
}
}
I have some code which looks like this to get an instance of Class. I am aware this is the service locator anti pattern, but we have no choice in this case due to other constraints:
var settings = new Settings();
var instance = Ioc.Instance.Get<Class>("settings", settings);
I would like to refactor it to look like this so that it is strongly typed, using a lambda to specify which argument on the constructor I am supplying:
var settings = new Settings();
var instance = Ioc.Instance.Get<Class>(x => x.settings, settings);
So, is this possible, and what would the code look like?
Conceptually there is a lack of the factory (factory interface), so it should be introduced to avoid using the container directly.
The Ninject Factory (factory interface) extension could be used to create the instance, as follows:
Declare a factory interface:
public interface IFactory
{
Class Create(Settings settings);
}
Add a binding to the composition root:
kernel.Bind<IFactory>().ToFactory();
Use the factory to get an instance:
var settings = new Settings();
var factory = Ioc.Instance.Get<IFactory>();
var instance = factory.Create(settings);
Please see the ninject/ninject.extensions.factory for the alternatives.
The problem with constructor argument names and expressions is, that an expression is only valid / complete when it covers all parameters of the constructor. Now i suppose you want to inject a few of the parameters (have ninject handle them) and for one or two specific parameters you want to pass a value, let's say it looks like:
public interface IFoo { }
public class Foo : IFoo
{
public Foo(IServiceOne one, IServiceTwo two, string parameter) {...}
}
Ninject supports ctor expressions, but only for bindings, and they work like this:
IBindingRoot.Bind<IFoo>().ToConstructor(x =>
new Foo(x.Inject<IServiceOne>(), x.Inject<IServiceTwo>(), "staticArgument");
so instead of only specifying the "staticArgument" which you are interested in, you also have to specify IServiceOne and IServiceTwo. What if the constructor changes? Well the call needs to be adapted as well! Lot of work for just passing a single simple parameter.
Now if you still want to do this i'd suggest having a look at the ToConstructor code and creating a similar extension for a Get call which will translate some call
IResolutionRoot.Get<IFoo>(x =>
new Foo(
x.Ignore<IServiceOne>(),
x.Ignore<IServiceTwo>(),
x.UseValue("mystring"));
to
IResolutionRoot.Get<IFoo>(new ConstructorArgument("parameter", "mystring"));
However, i would suggest going with #Sergey Brunov 's answer and use Ninject.Extensions.Factory. Now I think your going to say that it's no good because you'll still have to specify the parameter name,.. which is not refactor safe and a hassle (no code completion...).
However, there's a solution to the problem: Instead of using a constructor argument which "matches" the name of the argument, you can use a type matching argument.
Ok, there's a catch. If you've got multiple arguments of the same type,.. well it won't work. But i think that's seldomly the case and you can still introduce a container data-class to address it:
public class FooArguments
{
string Argument1 { get; set; }
string Argument2 { get; set; }
}
Now how can you use type matching?
There's two ways:
Use a Func<string, IFoo> factory. Just inject Func<string, IFoo> into where you want to create and IFoo.
Extend the Factory extension. Yes you've heard right ;-) It's actually not that difficult. You "just" need to implement a custom IInstanceProvider (also see http://www.planetgeek.ch/2011/12/31/ninject-extensions-factory-introduction/) so you can something like:
public interface IFooFactory
{
IFoo Create([MatchByType]string someParam, string matchByName);
}
(==> use an attribute to tell the factory extension how to pass the parameter to the Get<IFoo>request).
Look at following article -
http://handcraftsman.wordpress.com/2008/11/11/how-to-get-c-property-names-without-magic-strings/
Specifically
public static class Extensions
{
public static string GetPropertyName<T,TReturn>(this Expression<Func<T,TReturn>> expression)
{
MemberExpression body = (MemberExpression)expression.Body;
return body.Member.Name;
}
}
Note - This method may not work for all possible ways in which one can use expressions to indicate a property name for a class, and hence may need to be enhanced based on your needs (how generic you need it to be).
But essentially, once you have this helper method, your call becomes
var settings = new Settings();
Ioc.Instance.Get<Class>(GetPropertyName(x => x.settings), settings);

Can I invoke a method on a c# dynamic using a string method name? [duplicate]

This question already has answers here:
Calling a function from a string in C#
(5 answers)
Closed 9 years ago.
I was wondering if it was possible to call a function on a dynamic c# object via a string. I want to do something like this:
string lMethodName = "MethodName";
dynamic lDynamicObject = GetDynamicObject();
lDynamicObject.Invoke(lMethodName, lParameters);
Any thoughts or solutions would be greatly appreciated.
Maybe like Servy said there's a better way to achieve what we want through a different design. We're using SignalR and have a dashboard web app. We want each widget inside the dashboard to have the ability to have data pushed to it. To achieve this we were going to use razor code to inject the widget id into each individual widgets SignalR clientside proxy. Each widget would have something like the following:
hub.client.updateWidget123456 = aUpdateFunction;
Where 123456 is the id of the widget. On the serverside SignalR hub code we could call back into the clientside javascript function:
int lWidgetId = 123456;
dynamic lDynamic = Clients.All;
lDynamic.Invoke("updateWidget" + lWidgetId, lParameters);
There are other ways I can think of to implement this without creating the javascript proxy methods. I thought this was nice because the server had a direct communication line with each individual widget. Which could be accessed from multiple dashboards/browsers but they would all get updated via the single call above.
We could implement this instead by calling some other object in our javascript clientside code who knew about all the widgets and how to direct information to them. I felt like this went against the hub architecture of SignalR and like we were reinventing the wheel by doing this.
Does anyone have any thoughts on a better design approach? Thanks for the help and thanks for your comment Servy for sparking this discussion.
Also, this question is not a duplicate. The other question is not with regards to dynamic objects like Paul commented on below.
You can only use reflection to invoke a method on a dynamic if you know that it is an ordinary C# object (i.e. no special runtime binder).
Consider the following class that implements a property bag that can be used dynamically:
public class PropertyBag : DynamicObject
{
private Dictionary<string, object> _propertyBag = new Dictionary<string, object>();
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
return _propertyBag.TryGetValue(binder.Name, out result);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
_propertyBag[binder.Name] = value;
return true;
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return base.GetDynamicMemberNames();
}
}
It is perfectly valid to do the following:
dynamic propertyBag = new PropertyBag();
propertyBag.Name = "stackoverflow";
propertyBag.Value = 42;
If however you try to get the PropertyInfo for any of these dynamic properties it will fail because the PropertyBag class does not implement them:
public static void SetProperty(dynamic propertyBag, string propertyName, object value)
{
PropertyInfo property = propertyBag.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
if (property == null)
{
throw new NotImplementedException();
}
MethodInfo setProperty = property.GetSetMethod();
setProperty.Invoke(propertyBag, new object[] { value });
}
This will throw an exception:
dynamic propertyBag = new PropertyBag();
propertyBag.Name = "stackoverflow; // works as expected
SetProperty(propertyBag, "Value", 42); // throws a NotImplementedException
I used properties in this example because they are easier to implement but obviously the same applies to methods too. The reason for this is that dynamic is dynamically evaluated at runtime but reflection can only reflect on the static implementation. (What properties should GetProperties return on the PropertyBag class?)
If you are using dynamic with ordinary C# objects reflection will work as expected but you cannot rely on that if you don't know what type of runtime binder you are adressing and how it is implemented internally.
By reflection I think you can do something like that (if it's private ->NonPublic)
MethodInfo dynMethod = this.GetType().GetMethod("MethodName" + itemType, BindingFlags.NonPublic | BindingFlags.Instance);
dynMethod.Invoke(this, new object[] { methodParams });
Simply use the overloaded version of GetMethod that accepts BindingFlags:
Short answer, yes, you can do something like this:
class MyClass { public void Action() { Console.WriteLine("Do action"); } }
// ...
dynamic instance = new MyClass();
instance.GetType().GetMethod("Action").Invoke(instance, null);
However in this case, the fact that the instance is a dynamic type does absolutely nothing. You can use an object type and it will work the same way. The first method in the chain, GetType() returns the typeof(MyClass) and then it's plain old fasion reflection from there on out.
Using the dynamic keyword you can do this, which is far more interesting, albeit slightly less flexible at runtime:
dynamic instance = new MyClass();
instance.Action();
This will lookup the action at runtime using reflection. It isn't so obvious in this example that dynamic works differently from var because the MyClass is set here, but you could use the second line to execute ANY method named action so long as the instance had a method with that name.
Yes it is possible. What you want to do is called "reflection" and is available in most higher level languages
Here is an example of reflection in C. It is actually very powerful and allows you to make your code "aware of itself" up to a certain degree

Getting property type and pass to generic method

Working in .Net 4.5.
I am making classes that encapsulate an ActiveX control (CadCorp SIS activeX control to be specific) and basically replicate some of the objects available internally to that control (but not externally). From the outside the properties of the internal objects have to be manipulated using an API which takes text strings.
In each class I end up writing properties for getting and setting values via the API and i's basically the same code over and over again, e.g. Control.GetInt(TargetDescripor, TargetIdentifier, PropertyName);, so I am trying to use generics to cut down code to a minimum. I now have a method like this:
public T GetPropertyValue<T>(ObjectTypes Target, string PropertyName, int TargetIdentifier = 0)
which identifies the correct API method and returns the required value.
I still have to call this method from every object with the correct descriptor and the correct property name. I have cut down on that further. For example if I get a property in one of the objects I use the following code:
public Class MyObject
{
public bool MyObjectPropertyX
{
get { return this.GetProperty<bool>(); }
}
private const string _MyObjectPropertyX = "APICommandString";
private T GetPropertyValue<T>([CallerMemberName] string PropertyName = null)
{
string apiCommand = (string)this.GetType().GetField("_" + PropertyName, BindingFlags.NonPublic | BindingFlags.Static).GetValue(this);
// Call the method which executes the API command with the correct object
// descriptor and get the value
}
}
and this works just great.
Now I am wondering if it is possible in the property's getter to call the this.GetProperty<T>() with the type parameter being set automatically to the type of the property?
Is that feasible? Or is what I've got now as good as it gets?
UPDATE
Also, I would be interested to know if there are any drawbacks to moving to this kind of method. I will have to make a LOT of API calls, so I am wondering if using reflection will actually slow this down compared to the original code where I called the appropriate method explicitly in each getter and setter?
To address your first point, I don't think you will reduce the get code further without over complicating things; just specifiying the type seems fine to me.
If you want to be able to determine the property name without hardcoding a string, you can use This method with reflection.
On performance, I would say above all: Test it. If you find it is slow, then you could try caching you lookup of property actions. This code will wrap the reflected field.getValue call in a Func<string> so that the reflection lookup is not needed everytime. Bear in mind that the reflection api does some caching internally anyway, so there may be little benefit to this.
private readonly IDictionary<String, Func<String>> _cache = new Dictionary<String, Func<String>>();
private String GetApiCommand(String propertyName)
{
Func<String> command;
if (_cache.TryGetValue(propertyName, out command))
{
return command();
}
var field = GetType().GetField("_" + propertyName, BindingFlags.NonPublic | BindingFlags.Static);//.GetValue(this);
if (field != null)
{
Func<String> action = () => (String)field.GetValue(this);
_cache[propertyName] = action;
return action();
}
throw new NotSupportedException();
}

Strongly-typed property reference to multiple classes with no common interface (C#)

The System.Windows.Documents namespace includes a number of classes with an Inlines property of type InlineCollection. For example, the Paragraph, Bold and Hyperlink classes all have this property.
Each of these classes is decorated with ContentPropertyAttribute ...
[ContentPropertyAttribute("Inlines")]
public class Paragraph : Block
... which means that it is easy enough, using reflection, to detect that a given object exposes this property.
However, I need to be able to access this property in a strongly-typed manner across a selection of the types that implement it.
I am a little surprised that Microsoft didn't make all these classes implement an "IInlineContainer" interface, which would have made type checking and casting very easy.
However, in the absence of such an interface, is there any way to fake this polymorphic functionality, ideally without littering my code with lots of conditions and type checking?
Many thanks for your ideas,
Tim
Edit:
Thanks for your suggestions. A number of people have suggested the idea of a wrapper class, but this is not possible in my situation, as the target objects are not created by my code, but by the other classes in the .NET framework, for example the Xaml parser or the RichTextBox control (in which the containing FlowDocument is being edited).
Edit 2:
There have been several great suggestions here and I thank everyone who shared their ideas. The solution I have chosen to implement employs extension methods, which was suggested by #qstarin, although I have refined the concept to suit my needs, as follows:
public static InlineCollection GetInlines(
this FrameworkContentElement element)
{
if (element == null) throw new ArgumentNullException("element");
if (element is Paragraph)
{
return ((Paragraph) element).Inlines;
}
else if (element is Span) // also catches Bold, Italic, Unerline, Hyperlink
{
return ((Span)element).Inlines;
}
else
{
return null;
}
}
Although this approach requires conditional logic and type casting (which I said I wanted to avoid) the use of extension methods means that it only needs to be implemented in one place, leaving my various calling methods uncluttered.
Extension methods.
public static class InlineContainerExtensions {
public static InlineContainer GetInlines(this Paragraph inlineContainer) {
return inlineContainer.Inlines;
}
public static InlineContainer GetInlines(this Bold inlineContainer) {
return inlineContainer.Inlines;
}
}
If you didn't need to access it in a strongly-typed manner, but just without reflection, you could use dynamic:
dynamic doc = new Bold()
doc.InlineCollection. ...
doc = new Paragraph()
doc.InlineCollection. ...
Another option is to define a wrapper, that exposes a property with the same name, and has an overloaded constructor that takes Bold, Paragraph, etc.
You could implement a wrapper class that exposes an Inlines property and delegates via reflection to the contained object.
Decide if you want to validate that the wrapped object indeed has Inlines in your constructor or when trying to reference it
Employ the Adapter Pattern, write one class for each of those classes you wish to handle, effectively wrapping them in a layer implementing a common layer.
To make the classes discoverable, I would use reflection, tag each such class with an attribute for which class they handle, ie.:
[InlineContainerAdapter(typeof(SpecificClass1))]
public class WrapSpecificClass1 : IInlineContainer
and use reflection to find them.
This would give you several benefits:
You don't have to deal with dynamic, or similar solutions
While you have to use reflection to find the classes, the code you're actually executing once you've created the adapter is 100% yours, hand-coded
You can create adapters for classes that doesn't really implement what you need in the same manner as the rest, by just writing the adapter different
If this sounds like an interesting solution, leave a comment and I'll put up a working complete example.
One way of doing this (apart from using dynamic, which is the easiest solution IMO), you can create dynamically generated methods to return the inlines:
Func<object, InlineCollection> GetInlinesFunction(Type type)
{
string propertyName = ...;
// ^ check whether type has a ContentPropertyAttribute and
// retrieve its Name here, or null if there isn't one.
if (propertyName == null)
return null;
var p = Expression.Parameter(typeof(object), "it");
// The following creates a delegate that takes an object
// as input and returns an InlineCollection (as long as
// the object was at least of runtime-type "type".
return Expression.Lambda<Func<object, InlineCollection>>(
Expression.Property(
Expression.Convert(p, type),
propertyName),
p).Compile();
}
You'd have to cache these somewhere, though. A static Dictionary<Type, Func<object, InlineCollection>> comes to mind. Anyway, when you have, you can simply make an extension method:
public static InlineCollection GetInlines(this TextElement element)
{
Func<object, InlineCollection> f = GetCachedInlinesFunction(element.GetType());
if (f != null)
return f(element);
else
return null;
}
Now, with this in place, just use
InlineCollection coll = someElement.GetInlines();
Because you can check in your GetCachedInlinesFunction whether the property really exists or not, and handle that in a neat fashion, you won't have to litter your code with try catch blocks like you have to when you're using dynamic.
So, your dream-code would be:
foreach (var control in controls) {
var ic = control as IInlineContainer;
if (ic != null) {
DoSomething(ic.Inlines);
}
}
I don't see why you don't want to create a strongly typed wrapper class that uses reflection. With this class (no error handling):
public class InlinesResolver {
private object _target;
public InlinesResolver(object target) {
_target = target;
}
public bool HasInlines {
get {
return ResolveAttribute() != null;
}
}
public InlineCollection Inlines {
get {
var propertyName = ResolveAttribute().Name;
return (InlineCollection)
_target.GetType().GetProperty(propertyName).GetGetMethod().Invoke(_target, new object[] { });
}
}
private ContentPropertyAttribute ResolveAttribute() {
var attrs = _target.GetType().GetCustomAttributes(typeof(ContentPropertyAttribute), true);
if (attrs.Length == 0) return null;
return (ContentPropertyAttribute)attrs[0];
}
}
You could almost get to your dream-code:
foreach (var control in controls) {
var ir = new InlinesResolver(control);
if (ir.HasInlines) {
DoSomething(ir.Inlines);
}
}
You could always superclass them (e.g. InlineParagraph, InlineBold, etc) and have each of your superclasses implement an IInlineContainer interface like you suggested. Not the quickest or cleanest solution, but you at least have them all descending from the same interface.
Depending on your use-case, you could create a public Api that delegated its work to a private method that takes a dynamic. This keeps the strong typing for your public Api and eliminates code duplication, even though it falls back to using dynamic internally.
public void DoSomethingwithInlines(Paragraph p) {
do(p);
}
public void DoSomethingwithInlines(Bolb b) {
do(b);
}
private void do(dynamic d) {
// access Inlines here, using c# dynamic
}

Categories

Resources