كيف يمكنني التعبير عن استدعاء طريقة فراغ نتيجة dynamicmetaobject.bindinvokember؟

StackOverflow https://stackoverflow.com/questions/1835912

سؤال

أحاول إعطاء مثال قصير IDynamicMetaObjectProvider للطبعة الثانية من C # في العمق، وأنا أهرب إلى القضايا.

أريد أن أكون قادرا على التعبير عن مكالمة باطلة، وأنا فشلت. أنا متأكد من أنه من الممكن، لأنه إذا قمت باستدعاء طريقة باطلة دينامة باستخدام Binder الانعكاس، كل شيء على ما يرام. إليك مثال قصير ولكنه كامل:

using System;
using System.Dynamic;
using System.Linq.Expressions;

class DynamicDemo : IDynamicMetaObjectProvider
{
    public DynamicMetaObject GetMetaObject(Expression expression)
    {
        return new MetaDemo(expression, this);
    }

    public void TestMethod(string name)
    {
        Console.WriteLine(name);
    }

}

class MetaDemo : DynamicMetaObject
{
    internal MetaDemo(Expression expression, DynamicDemo demo)
        : base(expression, BindingRestrictions.Empty, demo)
    {
    }

    public override DynamicMetaObject BindInvokeMember
        (InvokeMemberBinder binder, DynamicMetaObject[] args)
    {
        Expression self = this.Expression;

        Expression target = Expression.Call
            (Expression.Convert(self, typeof(DynamicDemo)),
             typeof(DynamicDemo).GetMethod("TestMethod"),
             Expression.Constant(binder.Name));

        var restrictions = BindingRestrictions.GetTypeRestriction
            (self, typeof(DynamicDemo));

        return new DynamicMetaObject(target, restrictions);
    }
}

class Test
{
    public void Foo()
    {
    }

    static void Main()
    {
        dynamic x = new Test();
        x.Foo(); // Works fine!

        x = new DynamicDemo();
        x.Foo(); // Throws
    }
}

هذا يلقي استثناء:

استثناء غير معالج: System.InvalidcastException: نوع النتيجة "System.Void" من الربط الديناميكي الناتج عن كائن مع نوع "DynamicDemo" ل Binder 'Microsoft.csharp.runtimebinder.csharpinvokememberbinder' غير متوافق مع نظام نوع النتائج. الكائن "المتوقع من قبل موقع الاتصال.

إذا قمت بتغيير الطريقة لإرجاع الكائن وإعادة فارغة، فإنه يعمل بشكل جيد ... لكنني لا أريد أن تكون النتيجة خالية، أريد أن تكون باطلة. يعمل هذا على ما يرام ل Bruction Binder (انظر الدعوة الأولى في الرئيسية)، لكنه فشل في كائني الديناميكي. أريد أن أعمل مثل Binde Binder - من الجيد استدعاء الطريقة، طالما أنك لا تحاول استخدام النتيجة.

هل فاتني نوع معين من التعبير يمكنني استخدام الهدف؟

هل كانت مفيدة؟

المحلول

هذا مشابه ل:

عودة DLR نوع

تحتاج إلى مطابقة نوع الإرجاع المحدد بواسطة ReturnType خاصية. بالنسبة لجميع الثنائيات القياسية، تم إصلاح ذلك للاعتراض لكل شيء أو باطل تقريبا (لعمليات الحذف). إذا كنت تعرف أنك تقوم بإجراء مكالمة باطلة سأقترح التفاف فيها في:

Expression.Block(
    call,
    Expression.Default(typeof(object))
);

تستخدم DLR لتكون LAX تماما حول ما يسمح به وسوف يوفر بعض الحد الأدنى من الإكراه تلقائيا. لقد تخلصنا من ذلك لأننا لم نرغب في توفير مجموعة من التوظيفات التي قد تكون أو لا تكون منطقية لكل لغة.

يبدو وكأنك تريد منع:

dynamic x = obj.SomeMember();

لا توجد طريقة للقيام بذلك، ستكون هناك دائما قيمة عادت أن المستخدم يمكن للمستخدم محاولة الاستمرار في التفاعل مع ديناميكيا.

نصائح أخرى

أنا لا أحب هذا، لكن يبدو أن العمل؛ يبدو أن المشكلة الحقيقية هي binder.ReturnType الدخول بشكل غريب (وعدم إسقاطه ("البوب") تلقائيا)، ولكن:

if (target.Type != binder.ReturnType) {
    if (target.Type == typeof(void)) {
        target = Expression.Block(target, Expression.Default(binder.ReturnType));
    } else if (binder.ReturnType == typeof(void)) {
        target = Expression.Block(target, Expression.Empty());
    } else {
        target = Expression.Convert(target, binder.ReturnType);
    }
}
return new DynamicMetaObject(target, restrictions);

ربما تتوقع المكالمات أن تعاد خالية من إرجاعها ولكنها تراجع النتيجة - يبدو أن هذا العادة مثيرا للاهتمام، وخاصة العلم "الأساسي" ...

[Flags, EditorBrowsable(EditorBrowsableState.Never)]
public enum CSharpBinderFlags
{
    BinaryOperationLogical = 8,
    CheckedContext = 1,
    ConvertArrayIndex = 0x20,
    ConvertExplicit = 0x10,
    InvokeSimpleName = 2,
    InvokeSpecialName = 4,
    None = 0,
    ResultDiscarded = 0x100,
    ResultIndexed = 0x40,
    ValueFromCompoundAssignment = 0x80
}

غذاء للفكر...

تحديث:

يمكن جمع المزيد من التلميحات من Microsoft / Csharp / RuntimeBinder / DynamicmetaoBjectProviderdebugview والتي يتم استخدامها (أفترض) كمرئي للمصرفين. تقوم طريقة Tryevalmethodvarargs بفحص المفوض ويخلق بيندر مع النتيجة العلم المهملة (؟؟؟)

 Type delegateType = Expression.GetDelegateType(list.ToArray());
    if (string.IsNullOrEmpty(name))
    {
        binder = new CSharpInvokeBinder(CSharpCallFlags.ResultDiscarded, AccessibilityContext, list2.ToArray());
    }
    else
    {
        binder = new CSharpInvokeMemberBinder(CSharpCallFlags.ResultDiscarded, name, AccessibilityContext, types, list2.ToArray());
    }
    CallSite site = CallSite.Create(delegateType, binder);

... أنا في نهاية العاكس - فو هنا، لكن تأطير هذا الرمز يبدو غريبا بعض الشيء نظرا لأن طريقة TryeValmethodvarargs نفسها تتوقع كائن كوعي عائد، ويعود السطر النهائي نتيجة الدعوة الديناميكية وبعد ربما أكون نباح شجرة خاطئة [تعبير].

-

يعرف Binder C # (في Microsoft.csharp.dll) ما إذا كانت النتيجة المستخدمة أم لا؛ كما يقول x0n (+1)، فإنه يتتبع ذلك في علم. لسوء الحظ، يتم دفن العلم داخل CSharpInvokeMemberBinder المثال، وهو نوع خاص.

يبدو وكأنه آلية C # ربط ICSharpInvokeOrInvokeMemberBinder.ResultDiscarded (خاصية على واجهة داخلية) لقراءتها؛ CSharpInvokeMemberBinder ينفذ الواجهة (والممتلكات). يبدو أن الوظيفة قد تتم في Microsoft.CSharp.RuntimeBinder.BinderHelper.ConvertResult(). وبعد هذه الطريقة لديها رمز يلقي إذا كانت المذكورة أعلاه ResultDiscarded العقار لا يعود صحيحا إذا كان نوع التعبير باطلا.

لذلك لا ينظر إلي مثل هناك طريقة سهلة لتسريح حقيقة أن نتيجة التعبير يتم إسقاطها من Binder C #، في بيتا 2 على الأقل.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top