Reputation: 1661
I just can't seem to piece the following code together in the source code of MVC Webgrid.
When we are constructing the grid column, we do say
grid.Column("Id", format: (item) => item.GetSelectLink(item.Id)),
"item" is a lambda parameter obviously and it's actually a "WebGridRow" class. (I THINK!! UNLESS I'm WRONG) See source code here
https://github.com/mono/aspnetwebstack/blob/master/src/System.Web.Helpers/WebGrid/WebGridRow.cs
My issue here is, "Id" is not a property of this class and since WebGridRow inherits from DynamicObject, how exactly the ".Id" property mapped to the "source object" of the current row?
Btw, the source object "object value" is passed through the constructor of WebGridRow
public WebGridRow(WebGrid webGrid, object value, int rowIndex)
{
_grid = webGrid;
_value = value;
_rowIndex = rowIndex;
_dynamic = value as IDynamicMetaObjectProvider;
}
Column definition
public Func<dynamic, object> Format { get; set; }
Column is invoked in "WeBGridRenderer" class as
foreach (var row in webGrid.Rows)
{
.....
foreach (var column in columns)
{
var value = ...Format(column.Format, row).ToString();
...
}
}
Finally, the "Format" function in "WeBGridRenderer"
private static HelperResult Format(Func<dynamic, object> format, dynamic arg) {
var result = format(arg);
....
}
Upvotes: 2
Views: 589
Reputation: 12423
[Edit]
I've written a fairly extensive blog-post on the subject over here: http://blog.alxandr.me/2013/07/26/dynamic-dispatch-how-dynamic-work-in-c/
If you are interested in how dynamic dispatch in C# works, this might explain quite a bit.
[/Edit]
I've worked a lot with the DLR, and it's a huge and complicated beast, but I'll try to keep this simple.
What happens when you do anything with a dynamic
in C# is that the compiler generates a dynamic call-site. If you decompile the code you'd end up with something like CallSite<Func<CallSite, object, object, object>>
in most cases (not functions, they tend to have more than 2 parameters). The call-site does a lot of magic that we don't need to get too much into, but when an object implements IDynamicMetaObjectProvider
the dynamic callsite (or the binder used rather) invokes GetMetaObject
(http://msdn.microsoft.com/en-us/library/system.dynamic.idynamicmetaobjectprovider.getmetaobject.aspx) on said object.
A DynamicMetaObject
's role is to describe how to perform dynamic operations against a given object. In your case, the dynamic operation is "read property named Id", so the callsite calls BindGetMember
on the dynamic meta object. This BindGetMember
can do anything it likes, however, in the case of DynamicObject
it calls TryGetMember
on the DynamicObject
itself.
Now, there's a lot of caching (and black magic) involved, that makes this efficient, and DynamicObject
provides a simple way to implement IDynamicMetaObjectProvider
(which can be a real beast to get right at times), but that still doesn't mean that every dynamic
object is a DynamicObject
.
For instance, if I do this:
dynamic test = "test";
int length = test.Length;
test
(which is obviously a string) has no method TryGetMember
. However the CSharpGetMemberBinder
knows how to use reflection to figure out that string
has a property named Length
and return that.
Upvotes: 7
Reputation: 437336
When you try to access a property on a DynamicObect
, the DLR calls its TryGetMember
method at runtime (see how WebGridRow implements it). What you get back is that method's result, provided through its out
parameter.
A most important detail here is that your lambda function argument item
is not typed as WebGridRow
-- in that case this code would not compile. Dynamism is achieved because the signature of WebGrid.Column
declares the format
parameter as a Func<dynamic, object>
-- it's the dynamic
type of the argument that allows the DLR to take over.
Upvotes: 3