Передача динамического параметра вызывает исключение RuntimeBinderException при вызове метода из унаследованного интерфейса.

После некоторого рефакторинга возникла интересная проблема во время выполнения, которая привела к следующей ситуации.

При передаче свойства из динамического объекта в метод интерфейса, который был унаследован от родительского интерфейса, средство связывания во время выполнения не может найти метод.

Вот тест для демонстрации как неудачи, так и успеха (при вызове метода непосредственно в родительском типе интерфейса)

<code>using System.Dynamic;
using Microsoft.CSharp.RuntimeBinder;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Test.Utility
{
    public interface IEcho
    {
        string EchoString(string input);
    }

    public interface IInheritEcho : IEcho
    { }

    public class EchoClass : IInheritEcho
    {
        public string EchoString(string input)
        {
            return input;
        }
    }

    [TestClass]
    public class RuntimeBinderTest
    {
        [TestMethod]
        public void RuntimeBinder_should_work_when_dynamic_parameters_are_passed_to_method_from_inherited_interface()
        {
            //Arrange
            dynamic dynObject = new ExpandoObject();
            dynObject.Foo = "Bar";
            IInheritEcho echomore = new EchoClass();

            string echo = null;
            string exceptionMessage = null;

            //Act
            try
            {
                echo = echomore.EchoString(dynObject.Foo);
            }
            catch (RuntimeBinderException e)
            {
                exceptionMessage = e.Message;
            }

            //Assert
            Assert.AreEqual(echo, dynObject.Foo, false, exceptionMessage);
        }

        [TestMethod]
        public void RuntimeBinder_should_work_when_dynamic_parameters_are_passed_to_method_from_noninherited_interface()
        {
            //Arrange
            dynamic dynObject = new ExpandoObject();
            dynObject.Foo = "Bar";
            IEcho echomore = new EchoClass();

            string echo = null;
            string exceptionMessage = null;

            //Act
            try
            {
                echo = echomore.EchoString(dynObject.Foo);
            }
            catch (RuntimeBinderException e)
            {
                exceptionMessage = e.Message;
            }

            //Assert
            Assert.AreEqual(echo, dynObject.Foo, false, exceptionMessage);
        }
    }
}
</code>

Тест № 1 не пройден: Assert.AreEqual не удалось. Ожидаемое: & Lt; (нуль) & GT ;. Фактические :. & APOS; Test.Utility.IInheritEcho & APOS; не содержит определения для «EchoString»

Тест № 2 Успешен.

Мой вопрос заключается в том, верно ли мое предположение о том, что 1-й тест должен пройти, или в рамках есть фундаментальная причина, по которой он не проходит?

Я знаю, что могу решить эту проблему, приведя параметры, когда я передаю их, или назначая их переменным перед их передачей. Мне просто любопытно, почему унаследованный интерфейс вызывает сбой RuntimeBinder ...

 JotaBe10 мая 2012 г., 13:31
Если вы посмотрите на ссылку с ответом igofed на Microsof Connect, эта ошибка известна и, скорее всего, не будет устранена в новой версии VS.
 JotaBe10 мая 2012 г., 12:16
Вы совершенно правы. (Хотя это более старая версия C #:msdn.microsoft.com/en-us/library/aa664578%28VS.71%29.aspx) Должен быть сбой. Что если вы используете обычный класс со строковым свойством вместо объекта expando? Находит ли он метод в унаследованном интерфейсе? (У меня нет VS, чтобы сделать тест самостоятельно). Если это было со статическим связыванием (привязка времени компиляции), то с динамическим связывателем (связыватель времени выполнения) что-то не так.
 piff10 мая 2012 г., 13:20
Использование обычного класса со строковым свойством проходит как положено. Это определенно средство связывания времени выполнения, которое не ведет себя ...

Ответы на вопрос(2)

Казалось бы, что он принимает тип выражения во время компиляции,IInheritEchoи не углубленный поиск членов унаследованных интерфейсов при поиске метода для динамического вызова.

И в идеале C # связующее время выполнения дляdynamic выражение должно вести себя так же, как компилятор C # - поэтому он должен видеть, чтоIEcho интерфейс наследуетсяIInheritEcho и это должно работать.

Мы можем проверить гипотезу - то есть, что это статическая типизация - выполнив это в первом тесте:

echo = ((dynamic)echomore).EchoString(dynObject.Foo);

Привет предсто - тест пройден.

Таким образом, проблема не в том, что динамическое связующее не может найти метод, а в том, что когда экземпляр, член которого вызывается динамически,statically typed как интерфейс, унаследованные интерфейсы не используются.

На мой взгляд, это поведение неверно.

Пожалуйста, Эрик Липперт ... будь милым ...

 04 мая 2016 г., 21:52
И через пять лет это все еще выходит за рамки?
 piff10 мая 2012 г., 13:36
Итак, известная проблема. Следует доthis question что является хорошей дискуссией по этому вопросу
 24 дек. 2013 г., 01:24
К вашему сведению, для тех, кто снова обнаружит это через Google, окончательное решение Microsoft об этой ошибке "закрыто, поскольку не будет исправлено". 6/04/2011 из-за того, что он был "распределен". бу. :(

Ваш ответ на вопрос