I don't think that using dynamic
type is a good idea here, because the syntax isn't going to look much better and you sacrifice the type safety (and IntelliSense) by using dynamic typing.
However, here is an example of what you can do. The idea is that you wrap objects into DynamicWrapper
(your monadic value :-)) that can either contain a null
value or an actual value. It would inherit from DynamicObject
and delegate all calls to the actual object (if there is any) or immediately return null
(that would be monadic bind):
class DynamicWrapper : DynamicObject {
public object Object { get; private set; }
public DynamicWrapper(object o) { Object = o; }
public override bool TryGetMember(GetMemberBinder binder, out object result) {
// Special case to be used at the end to get the actual value
if (binder.Name == "Value") result = Object;
// Binding on 'null' value - return 'null'
else if (Object == null) result = new DynamicWrapper(null);
else {
// Binding on some value - delegate to the underlying object
var getMeth = Object.GetType().GetProperty(binder.Name).GetGetMethod();
result = new DynamicWrapper(getMeth.Invoke(Object, new object[0]));
return true;
}
public static dynamic Wrap(object o) {
return new DynamicWrapper(o);
}
}
The example supports only properties and it uses reflection in a pretty inefficient way (I think it could be optimized using DLR). Here is an example how it works:
class Product {
public Product Another { get; set; }
public string Name { get; set; }
}
var p1 = new Product { Another = null };
var p2 = new Product { Another = new Product { Name = "Foo" } };
var p3 = (Product)null;
// prints '' for p1 and p3 (null value) and 'Foo' for p2 (actual value)
string name = DynamicWrapper.Wrap(p1).Another.Name.Value;
Console.WriteLine(name);
Note that you can chain the calls freely - there is only something special at the beginning (Wrap
) and at the end (Value
), but in the middle, you can write .Another.Another.Another...
as many times you want.