C# Dynamic Object call TryInvoke with an unknown array of arguments

I need to invoke a given DynamicObject with a given array of arguments. However, I am struggling on how exactly to do this.

What I would like to do:

using System.Dynamic;
using System.Reflection;

InvokeDynamicObject(new A(), ((object[])["asd", 1, null]));

object? InvokeDynamicObject(DynamicObject dyn, object?[] args)
{
    dyn.TryInvoke(/* what? */, args, out object? res);
    return res;
}

class A : DynamicObject
{
    public override bool TryInvoke(InvokeBinder binder, object?[]? args, out object? result)
    {
        Console.WriteLine($"Called with args {String.Join(", ", (args ?? []).Select(a => a is null ? "null" : a.GetType().Name))}");
        result = null;
        return true;
    }
}

If the arguments were fixed it would be easy ((dynamic)dyn)(arg1, arg2, arg3) but how do I do this for an array of provided args?

I think I need to call TryInvoke using reflection, but what do I provide for the InvokeBinder argument? I didn’t find any way to create one, do I need to implement one myself (and what would that need to do?)?

8

First, you should understand that you could never exactly replicate what a compiler would generate for the same list of arguments. The compiler has access to compile-time type information, which could affect the binding process. With a object?[], you don’t have access to that. F("foo") and F((object)"foo") might bind to totally different methods, but your InvokeDynamicObject method cannot distinguish between them. Your method doesn’t know the compile time type of the arguments.

Second, I encourage you to check out on SharpLab, how a dynamically bound call is done under the hood.

  1. The compiler creates an array of CSharpArgumentInfos. One for each argument, and an extra one for the object on which you are calling the method.
  2. A CallSiteBinder is created by calling Microsoft.CSharp.RuntimeBinder.Binder.Invoke. This method takes some flags, the type in which this call occurs (for access control purposes), and the argument infos created in step 1
  3. The CallSiteBinder is used to create a CallSite<T>, where T is a delegate type. This will be a different delegate type depending on the types and number of arguments. For example, for the argument list (1,"foo",true), T would be Func<CallSite, object, int, string, bool, object>. The first object is the type of the receiver, and the second object is the return type.
  4. The CallSite is stored in a static field so that it can be reused.
  5. CallSite.Target is invoked with the corresponding parameters.

You’d need to do each of these steps dynamically. The most difficult one is probably 3, since you need to find an appropriate delegate type. Func only goes up to 16 parameters, so for calls with more than 14 arguments, you need to load your own delegate types dynamically!

That aside, here is a basic outline. Be aware that I’ve used some !s here.

static object? InvokeDynamicObject(DynamicObject dyn, object?[] args)
{
    // step 1
    var argInfos = Enumerable.Repeat(CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), args.Length + 1).ToArray();

    // here I have used the runtime type of the arguments, and typeof(object) if the argument is null
    var argTypes = args.Select(x => x?.GetType() ?? typeof(object)).ToArray();

    // this creates the T in CallSite<T>
    var callSiteDelegate = Type.GetType($"System.Func`{args.Length + 3}")!.MakeGenericType([typeof(CallSite), typeof(object), ..argTypes, typeof(object)]);

    // steps 2 and 3, assuming this happens in a class called 'Program'
    var callsite = CallSite.Create(callSiteDelegate, Binder.Invoke(CSharpBinderFlags.None, typeof(Program), argInfos));

    // step 5
    var field = typeof(CallSite<>).MakeGenericType(callSiteDelegate).GetField("Target");
    return ((Delegate)field!.GetValue(callsite)!).DynamicInvoke([callsite, dyn, ..args]);
}

This doesn’t handle calls of more than 14 arguments, and it doesn’t cache the CallSite. You’d need to dynamically create new delegate types to support more than 14 arguments, possibly using things from System.Reflection.Emit. You might want to store the CallSites in a Dictionary<int, CallSite>, one for each arity.

1

As long as the TryInvoke implementation (like yours) doesn’t use it, you can pass null! for the InvokeBinder argument when calling it.

dyn.TryInvoke(null!, args, out object? res);

Overall your code seems fine and prints

Called with args String, Int32, null

when I run it with that one change.

If you wanted to pass extra call-site information or return type information to your dynamic method, you would have to subclass InvokeBinder and construct an instance yourself, but that’s optional and mainly used when bridging dynamic languages (JS, Python) to C#, I believe.

1

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật