I have an existing application that I am modifying to use Autofac Property Injection. It seems regardless of which method I use to register my types with properties, the properties are always null unless they have public setters. With other IoC containers (e.g. Structuremap) it's possible to scope the setter internal and make it available using the InternalsVisibleTo attribute on the assembly. This would seem nice to restrict clients from modifying the assignment.
Is this possible with Autofac? Or is there another approach when working with property injection to keep the assignments secure?
I've tried using reflection with PropertiesAutoWired() as well as resolving .WithParameter() from my WebApi Global.asax - specifying the specific parameter to be set with no success as an internal setter.
[assembly: InternalsVisibleTo("MyWebAPI.dll")]
[assembly: InternalsVisibleTo("Autofac.dll")]
[assembly: InternalsVisibleTo("Autofac.Configuration.dll")]
namespace My.Namespace
{
public class BaseContext
{
public MyPublicClass _dbHelper { get; internal set; }
public BaseContext()
{
}
protected string DbConnectionString
{
get
{
return _dbHelper.DbConn; //<-Always null unless setter is public
}
}
}
}
You cannot inject internal setters with autofac, because the AutowiringPropertyInjector class is only looking for public properties (see source).
However a logic in the AutowiringPropertyInjector is very simple so you can create your own version which does injection for non public properties:
public static class AutowiringNonPublicPropertyInjector
{
public static void InjectProperties(IComponentContext context,
object instance, bool overrideSetValues)
{
if (context == null)
throw new ArgumentNullException("context");
if (instance == null)
throw new ArgumentNullException("instance");
foreach (
PropertyInfo propertyInfo in
//BindingFlags.NonPublic flag added for non public properties
instance.GetType().GetProperties(BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic))
{
Type propertyType = propertyInfo.PropertyType;
if ((!propertyType.IsValueType || propertyType.IsEnum) &&
(propertyInfo.GetIndexParameters().Length == 0 &&
context.IsRegistered(propertyType)))
{
//Changed to GetAccessors(true) to return non public accessors
MethodInfo[] accessors = propertyInfo.GetAccessors(true);
if ((accessors.Length != 1 ||
!(accessors[0].ReturnType != typeof (void))) &&
(overrideSetValues || accessors.Length != 2 ||
propertyInfo.GetValue(instance, null) == null))
{
object obj = context.Resolve(propertyType);
propertyInfo.SetValue(instance, obj, null);
}
}
}
}
}
And now you can use this class in the OnActivated event
var builder = new ContainerBuilder();
builder.RegisterType<MyPublicClass>();
builder.RegisterType<BaseContext>()
.OnActivated(args =>
AutowiringNonPublicPropertyInjector
.InjectProperties(args.Context, args.Instance, true));
However the above listed solution now injects all kind of properties so even private and protected ones so you may need to extend it with some additional checks to make sure that you will only inject the properties what you would expect.
I'm using a solution like this:
builder.RegisterType<MyPublicClass>();
builder.RegisterType<BaseContext>()
.OnActivating(CustomPropertiesHandler);
With a handler like this:
//If OnActivated: Autofac.Core.IActivatedEventArgs
public void CustomPropertiesHandler<T>(Autofac.Core.IActivatingEventArgs<T> e)
{
var props = e.Instance.GetType()
.GetTypeInfo().DeclaredProperties //Also "private prop" with "public set"
.Where(pi => pi.CanWrite) //Has a set accessor.
//.Where(pi => pi.SetMethod.IsPrivate) //set accessor is private
.Where(pi => e.Context.IsRegistered(pi.PropertyType)); //Type is resolvable
foreach (var prop in props)
prop.SetValue(e.Instance, e.Context.Resolve(prop.PropertyType), null);
}
Since both IActivatingEventArgs and IActivatedEventArgs has instance and context, you might want to use wrapping methods that uses those parameters on CustomPropertiesHandler instead.
Also we can write #nemesv implementation as an extension method.
public static class AutofacExtensions
{
public static void InjectProperties(IComponentContext context, object instance, bool overrideSetValues)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (instance == null)
{
throw new ArgumentNullException(nameof(instance));
}
foreach (var propertyInfo in instance.GetType().GetProperties(BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic))
{
var propertyType = propertyInfo.PropertyType;
if ((!propertyType.IsValueType || propertyType.IsEnum) && (propertyInfo.GetIndexParameters().Length == 0) && context.IsRegistered(propertyType))
{
var accessors = propertyInfo.GetAccessors(true);
if (((accessors.Length != 1) ||
!(accessors[0].ReturnType != typeof(void))) &&
(overrideSetValues || (accessors.Length != 2) ||
(propertyInfo.GetValue(instance, null) == null)))
{
var obj = context.Resolve(propertyType);
propertyInfo.SetValue(instance, obj, null);
}
}
}
}
public static IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> InjectPropertiesAsAutowired<TLimit, TActivatorData, TRegistrationStyle>(
this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> registration)
{
return registration.OnActivated(args => InjectProperties(args.Context, args.Instance, true));
}
To Use;
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<StartupConfiguration>().As<IStartupConfiguration>().AsSelf().InjectPropertiesAsAutowired().AsImplementedInterfaces().SingleInstance();
}
Current version of Autofac defined optional IPropertySelector parameter for PropertiesAutowired which is used to filter out injectable properties.
default implementation for IPropertySelector is DefaultPropertySelector, which filters non public properties.
public virtual bool InjectProperty(PropertyInfo propertyInfo, object instance)
{
if (!propertyInfo.CanWrite || propertyInfo.SetMethod?.IsPublic != true)
{
return false;
}
....
}
define custom IPropertySelector which allows injection to non public properties
public class AccessRightInvariantPropertySelector : DefaultPropertySelector
{
public AccessRightInvariantPropertySelector(bool preserveSetValues) : base(preserveSetValues)
{ }
public override bool InjectProperty(PropertyInfo propertyInfo, object instance)
{
if (!propertyInfo.CanWrite)
{
return false;
}
if (!PreserveSetValues || !propertyInfo.CanRead)
{
return true;
}
try
{
return propertyInfo.GetValue(instance, null) == null;
}
catch
{
// Issue #799: If getting the property value throws an exception
// then assume it's set and skip it.
return false;
}
}
}
Use
builder.RegisterType<AppService>()
.AsImplementedInterfaces()
.PropertiesAutowired(new AccessRightInvariantPropertySelector(true));
Alternatively
Install
PM> Install-Package Autofac.Core.NonPublicProperty
Use
builder.RegisterType<AppService>()
.AsImplementedInterfaces()
.AutoWireNonPublicProperties();
Related
I have a web interface with a lot of data grids that allow the user passing a search criteria to the server.
I don't want to actually build an explicit method that will handle each grid individually, so I thought to allow passing JSON criteria from the client to the server.
This code
string res = JsonConvert.SerializeObject(Restrictions.Eq("id", "1"));
will return will return {"PropertyName":"id","Value":"1"} which is exactly what I wanted to pass from the client - perfect.
but this code with or statement:
string res = JsonConvert.SerializeObject(Restrictions.Or(Restrictions.Eq("id", "1"),Restrictions.Eq("id", "2")))
will return {}
What is the way to convert JSON into a Criteria and back in nHibernate.
To be more accurate, I have something like this in the client, and I want my server to support it. I can serialize the client ui to json.
Thanks
The empty json serialization happens because newtonsoft can only serialize public fields.
The code below will serialize all fields of a class regardless of its visibility.
public class MyContractResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(p => base.CreateProperty(p, memberSerialization))
.Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(f => base.CreateProperty(f, memberSerialization)))
.ToList();
props.ForEach(p => { p.Writable = true; p.Readable = true; });
return props;
}
}
...
var myOrs = Restrictions.Or(Restrictions.Eq("id", "1"), Restrictions.Eq("id", "2"));
var settings = new JsonSerializerSettings()
{
ContractResolver = new MyContractResolver()
};
string json = JsonConvert.SerializeObject(myOrs, settings);
Now to deserialize this json we have a problem, the NHibernate classes are either abstract or don't have a public empty constructor.
You will need to create a custom class to deserialize.
public class MyConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException("Not implemented yet");
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject obj = JObject.Load(reader);
if (obj.Value<string>("Op") == "or")
{
var leftJObject = obj["LeftHandSide"];
var rightJObject = obj["RightHandSide"];
var left = new SimpleExpression(leftJObject.Value<string>("PropertyName"), GetJTokenValue(leftJObject["Value"]), leftJObject.Value<string>("Op"));
var right = new SimpleExpression(rightJObject.Value<string>("PropertyName"), GetJTokenValue(rightJObject["Value"]), rightJObject.Value<string>("Op"));
return Restrictions.Or(left, right);
}
//TODO: The rest of the restrictions
return null;
}
public override bool CanWrite
{
get { return false; }
}
public override bool CanConvert(Type objectType)
{
return true;
}
private object GetJTokenValue(JToken token)
{
if (token.Type == JTokenType.String)
{
return token.Value<string>();
}
else if (token.Type == JTokenType.Integer)
{
return token.Value<Int32>();
}
//TODO: The rest of the types
return null;
}
}
...
var result = JsonConvert.DeserializeObject<AbstractCriterion>(json, new MyConverter());
UPDATE: the interceptor is indeed called. However, if I set a breakpoint in the intercepting method, the breakpoint is not triggered (although the method is indeed called). If I set the breakpoint in a method called from the interceptor, then it is triggered (because the breakpoint was not triggered in the first case, this is what made me think initially that the interceptor was not called)
I am trying to use ByteBuddy in order to implement a proxy a class for tracking all changes on an Entity as follows:
public class EntityProxyGenerator{
public static <T extends Entity> T createProxy(T entity) throws NoSuchMethodException, InstantiationException, IllegalAccessException,
InvocationTargetException {
EntityChangesInterceptor interceptor = new EntityChangesInterceptor(entity);
_class = entity.getClass();
Class Proxy =
new ByteBuddy()
.subclass(_class)
.method(ElementMatchers.isSetter())
.intercept(MethodDelegation.to(interceptor))
.make()
.load(EntityProxyGenerator.class.getClassLoader())
.getLoaded();
return (T) Proxy.getDeclaredConstructor().newInstance();
}
}
And EntityChangesInterceptor is implemented as follows:
public class EntityChangesInterceptor<T extends Entity> {
private final T original;
public EntityChangesInterceptor(T original) {
this.original = original;
}
public boolean isValueObject(Object object) {
Class<?> class_ = object.getClass();
if (class_ == String.class
|| class_ == Integer.class
|| class_ == Double.class
|| class_ == Timestamp.class
|| class_ == Instant.class) {
return true;
}
return false;
}
boolean isPropertyGetter(Method method, Object[] args) {
return method.getName().startsWith("get") && args.length == 0;
}
boolean isPropertySetter(Method method, Object[] args) {
return method.getName().startsWith("set") && args.length == 1;
}
#RuntimeType
public Object intercept(#Origin Method method, #AllArguments Object[] args) throws Throwable {
try {
if (isPropertySetter(method, args)) {
if (isValueObject(args[0])) {
String propertyName = method.getName().substring(3);
String getter = "get" + propertyName;
Object oldValue = MethodUtils.invokeMethod(original, getter, null);
Object newValue = args[0];
ValueChange valueChange = new ValueChange(propertyName, oldValue, newValue);
Object callResult = method.invoke(original, args);
original.addPropertyChange(valueChange);
return callResult;
}
}
return method.invoke(original, args);
} finally {
// do your completion logic here
}
}
}
The proxy is created correctly, however whenever I try to call a setter on the proxy class, EntityChangesInterceptor.intercept is never called.
If I change the proxy class implementation so that it intercepts getters as follows, then everything works fine:
Class Proxy = new ByteBuddy()
.subclass(_class)
.method(ElementMatchers.isGetter()) // note isGetter() instead of isSetter()
.intercept(MethodDelegation.to(interceptor))
.make()
.load(EntityProxyGenerator.class.getClassLoader())
.getLoaded();
Making isValueObject private does the trick.
I recently upgraded an application I am working on from Cocktail 1.4 to Cocktail 2.6 (Punch). I have adjusted my bootstrapper class for the wpf project which now loads with no issues. However, on my WCF / Web projects, I am receiving a runtime exception with the following error when attempting to call Composition.GetInstance:
"You must first set a valid CompositionProvider by using Composition.SetProvider."
After digging into the issue a bit, it appears the composition container is automatically configured when your bootstrapper inherits from CocktailMefBootstrapper. I currently do not have bootstrapper classes at all for non-wpf projects. Prior to the upgrade, all I had to do was call the configure method on the Composition class to configure the composition container, but it appears that it has been deprecated:
Composition.Configure();
I noticed that you can also call Composition.SetProvider(), however I am a little unsure on how to satisfy the method signature exactly. The DevForce Punch documentation states that the generic type for the bootstrapper class should be a viewmodel, and there are no views / view models in a service project. This leaves me in limbo on what to do as I don't want to rip cocktail out of these WCF projects. Is there still a way to use Cocktail's composition container without a bootstrapper for a project in Cocktail (Punch) 2.6?
UPDATE
I found this on the DevForce forums. So it appears that I ought to learn how to configure a multi threaded ICompositionProvider and call Composition.SetProvider() as mentioned above. Any recommended articles to achieving this?
After digging through Punch's source code and looking at Ideablade's MefCompositionContainer, which implements ICompositionProvider, I created my own thread safe implementation of ICompositionProvider. Below is the code I used. Basically, it's the same code for Ideablade's MefCompositionContainer which can be found here in their repository. The only change is that I am passing a bool flag of true into the CompositionContainer's constructor. MSDN lists the pros and cons of making the container thread safe
internal partial class ThreadSafeCompositionProvider : ICompositionProvider
{
static ThreadSafeCompositionProvider()
{
CompositionHost.IgnorePatterns.Add("Caliburn.Micro*");
CompositionHost.IgnorePatterns.Add("Windows.UI.Interactivity*");
CompositionHost.IgnorePatterns.Add("Cocktail.Utils*");
CompositionHost.IgnorePatterns.Add("Cocktail.Compat*");
CompositionHost.IgnorePatterns.Add("Cocktail.dll");
CompositionHost.IgnorePatterns.Add("Cocktail.SL.dll");
CompositionHost.IgnorePatterns.Add("Cocktail.WinRT.dll");
}
public IEnumerable<Assembly> GetProbeAssemblies()
{
IEnumerable<Assembly> probeAssemblies = CompositionHost.Instance.ProbeAssemblies;
var t = GetType();
// Add Cocktail assembly
probeAssemblies = probeAssemblies.Concat(GetType().GetAssembly());
return probeAssemblies.Distinct(x => x);
}
private List<Assembly> _probeAssemblies;
private AggregateCatalog _defaultCatalog;
private ComposablePartCatalog _catalog;
private CompositionContainer _container;
public ComposablePartCatalog Catalog
{
get { return _catalog ?? DefaultCatalog; }
}
public ComposablePartCatalog DefaultCatalog
{
get
{
if (_defaultCatalog == null)
{
_probeAssemblies = GetProbeAssemblies().ToList();
var mainCatalog = new AggregateCatalog(_probeAssemblies.Select(x => new AssemblyCatalog(x)));
_defaultCatalog = new AggregateCatalog(mainCatalog);
CompositionHost.Recomposed += new EventHandler<RecomposedEventArgs>(OnRecomposed)
.MakeWeak(x => CompositionHost.Recomposed -= x);
}
return _defaultCatalog;
}
}
internal void OnRecomposed(object sender, RecomposedEventArgs args)
{
if (args.HasError) return;
var newAssemblies = GetProbeAssemblies()
.Where(x => !_probeAssemblies.Contains(x))
.ToList();
if (newAssemblies.Any())
{
var catalog = new AggregateCatalog(newAssemblies.Select(x => new AssemblyCatalog(x)));
_defaultCatalog.Catalogs.Add(catalog);
_probeAssemblies.AddRange(newAssemblies);
}
// Notify clients of the recomposition
var handlers = Recomposed;
if (handlers != null)
handlers(sender, args);
}
public CompositionContainer Container
{
get { return _container ?? (_container = new CompositionContainer(Catalog, true)); }
}
public Lazy<T> GetInstance<T>() where T : class
{
var exports = GetExportsCore(typeof(T), null).ToList();
if (!exports.Any())
throw new Exception(string.Format("Could Not Locate Any Instances Of Contract", typeof(T).FullName));
return new Lazy<T>(() => (T)exports.First().Value);
}
public T TryGetInstance<T>() where T : class
{
if (!IsTypeRegistered<T>())
return null;
return GetInstance<T>().Value;
}
public IEnumerable<T> GetInstances<T>() where T : class
{
var exports = GetExportsCore(typeof(T), null);
return exports.Select(x => (T)x.Value);
}
public Lazy<object> GetInstance(Type serviceType, string contractName)
{
var exports = GetExportsCore(serviceType, contractName).ToList();
if (!exports.Any())
throw new Exception(string.Format("Could Not Locate Any Instances Of Contract",
serviceType != null ? serviceType.ToString() : contractName));
return new Lazy<object>(() => exports.First().Value);
}
public object TryGetInstance(Type serviceType, string contractName)
{
var exports = GetExportsCore(serviceType, contractName).ToList();
if (!exports.Any())
return null;
return exports.First().Value;
}
public IEnumerable<object> GetInstances(Type serviceType, string contractName)
{
var exports = GetExportsCore(serviceType, contractName);
return exports.Select(x => x.Value);
}
public ICompositionFactory<T> GetInstanceFactory<T>() where T : class
{
var factory = new ThreadSafeCompositionFactory<T>();
Container.SatisfyImportsOnce(factory);
if (factory.ExportFactory == null)
throw new CompositionException(string.Format("No export found.", typeof(T)));
return factory;
}
public ICompositionFactory<T> TryGetInstanceFactory<T>() where T : class
{
var factory = new ThreadSafeCompositionFactory<T>();
Container.SatisfyImportsOnce(factory);
if (factory.ExportFactory == null)
return null;
return factory;
}
public void BuildUp(object instance)
{
// Skip if in design mode.
if (DesignTime.InDesignMode())
return;
Container.SatisfyImportsOnce(instance);
}
public bool IsRecomposing { get; internal set; }
public event EventHandler<RecomposedEventArgs> Recomposed;
internal bool IsTypeRegistered<T>() where T : class
{
return Container.GetExports<T>().Any();
}
public void Configure(CompositionBatch compositionBatch = null, ComposablePartCatalog catalog = null)
{
_catalog = catalog;
var batch = compositionBatch ?? new CompositionBatch();
if (!IsTypeRegistered<IEventAggregator>())
batch.AddExportedValue<IEventAggregator>(new EventAggregator());
Compose(batch);
}
public void Compose(CompositionBatch compositionBatch)
{
if (compositionBatch == null)
throw new ArgumentNullException("compositionBatch");
Container.Compose(compositionBatch);
}
private IEnumerable<Lazy<object>> GetExportsCore(Type serviceType, string key)
{
return Container.GetExports(serviceType, null, key);
}
}
After setting up that class, I added a configuration during startup to instantiate my new thread safe composition provider and to set it as the provider for Punch's Composition class:
if (createThreadSafeCompositionContainer)
{
var threadSafeContainer = new ThreadSafeCompositionProvider();
Composition.SetProvider(threadSafeContainer);
}
Seems to be working like a charm!
public bool IsList(object value)
{
Type type = value.GetType();
// Check if type is a generic list of any type
}
What's the best way to check if the given object is a list, or can be cast to a list?
For you guys that enjoy the use of extension methods:
public static bool IsGenericList(this object o)
{
var oType = o.GetType();
return (oType.IsGenericType && (oType.GetGenericTypeDefinition() == typeof(List<>)));
}
So, we could do:
if(o.IsGenericList())
{
//...
}
using System.Collections;
if(value is IList && value.GetType().IsGenericType) {
}
bool isList = o.GetType().IsGenericType
&& o.GetType().GetGenericTypeDefinition() == typeof(IList<>));
public bool IsList(object value) {
return value is IList
|| IsGenericList(value);
}
public bool IsGenericList(object value) {
var type = value.GetType();
return type.IsGenericType
&& typeof(List<>) == type.GetGenericTypeDefinition();
}
Here's an implementation that works in .NET Standard, and works against interfaces:
public static bool ImplementsGenericInterface(this Type type, Type interfaceType)
{
return type
.GetTypeInfo()
.ImplementedInterfaces
.Any(x => x.GetTypeInfo().IsGenericType && x.GetGenericTypeDefinition() == interfaceType);
}
And here are the tests (xunit):
[Fact]
public void ImplementsGenericInterface_List_IsValidInterfaceTypes()
{
var list = new List<string>();
Assert.True(list.GetType().ImplementsGenericInterface(typeof(IList<>)));
Assert.True(list.GetType().ImplementsGenericInterface(typeof(IEnumerable<>)));
Assert.True(list.GetType().ImplementsGenericInterface(typeof(IReadOnlyList<>)));
}
[Fact]
public void ImplementsGenericInterface_List_IsNotInvalidInterfaceTypes()
{
var list = new List<string>();
Assert.False(list.GetType().ImplementsGenericInterface(typeof(string)));
Assert.False(list.GetType().ImplementsGenericInterface(typeof(IDictionary<,>)));
Assert.False(list.GetType().ImplementsGenericInterface(typeof(IComparable<>)));
Assert.False(list.GetType().ImplementsGenericInterface(typeof(DateTime)));
}
if(value is IList && value.GetType().GetGenericArguments().Length > 0)
{
}
Based on Victor Rodrigues' answer, we can devise another method for generics. In fact, the original solution can be reduced to only two lines:
public static bool IsGenericList(this object Value)
{
var t = Value.GetType();
return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>);
}
public static bool IsGenericList<T>(this object Value)
{
var t = Value.GetType();
return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<T>);
}
I'm using the following code:
public bool IsList(Type type) => type.IsGenericType && (
(type.GetGenericTypeDefinition() == typeof(List<>))
|| (type.GetGenericTypeDefinition() == typeof(IList<>))
);
Probably the best way would be to do something like this:
IList list = value as IList;
if (list != null)
{
// use list in here
}
This will give you maximum flexibility and also allow you to work with many different types that implement the IList interface.
I'm wanting to have a simple duck typing example in C# using dynamic objects. It would seem to me, that a dynamic object should have HasValue/HasProperty/HasMethod methods with a single string parameter for the name of the value, property, or method you are looking for before trying to run against it. I'm trying to avoid try/catch blocks, and deeper reflection if possible. It just seems to be a common practice for duck typing in dynamic languages (JS, Ruby, Python etc.) that is to test for a property/method before trying to use it, then falling back to a default, or throwing a controlled exception. The example below is basically what I want to accomplish.
If the methods described above don't exist, does anyone have premade extension methods for dynamic that will do this?
Example: In JavaScript I can test for a method on an object fairly easily.
//JavaScript
function quack(duck) {
if (duck && typeof duck.quack === "function") {
return duck.quack();
}
return null; //nothing to return, not a duck
}
How would I do the same in C#?
//C# 4
dynamic Quack(dynamic duck)
{
//how do I test that the duck is not null,
//and has a quack method?
//if it doesn't quack, return null
}
If you have control over all of the object types that you will be using dynamically, another option would be to force them to inherit from a subclass of the DynamicObject class that is tailored to not fail when a method that does not exist is invoked:
A quick and dirty version would look like this:
public class DynamicAnimal : DynamicObject
{
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
bool success = base.TryInvokeMember(binder, args, out result);
// If the method didn't exist, ensure the result is null
if (!success) result = null;
// Always return true to avoid Exceptions being raised
return true;
}
}
You could then do the following:
public class Duck : DynamicAnimal
{
public string Quack()
{
return "QUACK!";
}
}
public class Cow : DynamicAnimal
{
public string Moo()
{
return "Mooooo!";
}
}
class Program
{
static void Main(string[] args)
{
var duck = new Duck();
var cow = new Cow();
Console.WriteLine("Can a duck quack?");
Console.WriteLine(DoQuack(duck));
Console.WriteLine("Can a cow quack?");
Console.WriteLine(DoQuack(cow));
Console.ReadKey();
}
public static string DoQuack(dynamic animal)
{
string result = animal.Quack();
return result ?? "... silence ...";
}
}
And your output would be:
Can a duck quack?
QUACK!
Can a cow quack?
... silence ...
Edit: I should note that this is the tip of the iceberg if you are able to use this approach and build on DynamicObject. You could write methods like bool HasMember(string memberName) if you so desired.
Try this:
using System.Linq;
using System.Reflection;
//...
public dynamic Quack(dynamic duck, int i)
{
Object obj = duck as Object;
if (duck != null)
{
//check if object has method Quack()
MethodInfo method = obj.GetType().GetMethods().
FirstOrDefault(x => x.Name == "Quack");
//if yes
if (method != null)
{
//invoke and return value
return method.Invoke((object)duck, null);
}
}
return null;
}
Or this (uses only dynamic):
public static dynamic Quack(dynamic duck)
{
try
{
//invoke and return value
return duck.Quack();
}
//thrown if method call failed
catch (RuntimeBinderException)
{
return null;
}
}
Implementation of the HasProperty method for every IDynamicMetaObjectProvider WITHOUT throwing RuntimeBinderException.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Dynamic;
using Microsoft.CSharp.RuntimeBinder;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
namespace DynamicCheckPropertyExistence
{
class Program
{
static void Main(string[] args)
{
dynamic testDynamicObject = new ExpandoObject();
testDynamicObject.Name = "Testovaci vlastnost";
Console.WriteLine(HasProperty(testDynamicObject, "Name"));
Console.WriteLine(HasProperty(testDynamicObject, "Id"));
Console.ReadLine();
}
private static bool HasProperty(IDynamicMetaObjectProvider dynamicProvider, string name)
{
var defaultBinder = Binder.GetMember(CSharpBinderFlags.None, name, typeof(Program),
new[]
{
CSharpArgumentInfo.Create(
CSharpArgumentInfoFlags.None, null)
}) as GetMemberBinder;
var callSite = CallSite<Func<CallSite, object, object>>.Create(new NoThrowGetBinderMember(name, false, defaultBinder));
var result = callSite.Target(callSite, dynamicProvider);
if (Object.ReferenceEquals(result, NoThrowExpressionVisitor.DUMMY_RESULT))
{
return false;
}
return true;
}
}
class NoThrowGetBinderMember : GetMemberBinder
{
private GetMemberBinder m_innerBinder;
public NoThrowGetBinderMember(string name, bool ignoreCase, GetMemberBinder innerBinder) : base(name, ignoreCase)
{
m_innerBinder = innerBinder;
}
public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion)
{
var retMetaObject = m_innerBinder.Bind(target, new DynamicMetaObject[] {});
var noThrowVisitor = new NoThrowExpressionVisitor();
var resultExpression = noThrowVisitor.Visit(retMetaObject.Expression);
var finalMetaObject = new DynamicMetaObject(resultExpression, retMetaObject.Restrictions);
return finalMetaObject;
}
}
class NoThrowExpressionVisitor : ExpressionVisitor
{
public static readonly object DUMMY_RESULT = new DummyBindingResult();
public NoThrowExpressionVisitor()
{
}
protected override Expression VisitConditional(ConditionalExpression node)
{
if (node.IfFalse.NodeType != ExpressionType.Throw)
{
return base.VisitConditional(node);
}
Expression<Func<Object>> dummyFalseResult = () => DUMMY_RESULT;
var invokeDummyFalseResult = Expression.Invoke(dummyFalseResult, null);
return Expression.Condition(node.Test, node.IfTrue, invokeDummyFalseResult);
}
private class DummyBindingResult {}
}
}
impromptu-interface seems to be a nice Interface mapper for dynamic objects... It's a bit more work than I was hoping for, but seems to be the cleanest implementation of the examples presented... Keeping Simon's answer as correct, since it is still the closest to what I wanted, but the Impromptu interface methods are really nice.
The shortest path would be to invoke it, and handle the exception if the method does not exist. I come from Python where such method is common in duck-typing, but I don't know if it is widely used in C#4...
I haven't tested myself since I don't have VC 2010 on my machine
dynamic Quack(dynamic duck)
{
try
{
return duck.Quack();
}
catch (RuntimeBinderException)
{ return null; }
}
Have not see a correct answer here, MS provides an example now with casting to a dictionary
dynamic employee = new ExpandoObject();
employee.Name = "John Smith";
employee.Age = 33;
foreach (var property in (IDictionary<String, Object>)employee)
{
Console.WriteLine(property.Key + ": " + property.Value);
}
// This code example produces the following output:
// Name: John Smith
// Age: 33