Почему динамический тип работает там, где нет литья? - PullRequest
3 голосов
/ 29 октября 2010

До сих пор я предполагал, что динамический тип просто «отключает» проверку типов во время компиляции и делает что-то похожее на приведение типов, когда сообщение вызывается в динамическом экземпляре. Очевидно, что-то еще происходит.

Прикрепленный тестовый пример NUnit показывает мою проблему: используя динамический тип, я могу использовать метод, доступный только в конкретном подклассе, но я не могу сделать то же самое с использованием приведения (приводит к InvalidCastException). Я бы предпочел сделать приведение, так как это дает мне полное завершение кода в VS.

Может кто-нибудь объяснить, что происходит, и / или дать мне подсказку, как я могу получить завершение кода в моем случае без необходимости переопределения метода WorkWithAndCreate в каждом конкретном подклассе?

ура, Йоханнес

using System;
using NUnit.Framework;

namespace SlidesCode.TestDataBuilder
{
    [TestFixture]
    public class MyTest
    {
        [Test]
        public void DynamicWorks()
        {
            string aString = CreateDynamic(obj => obj.OnlyInConcreteClass());
            Assert.AreEqual("a string", aString);
        }

        private static string CreateDynamic(Action<dynamic> action)
        {
            return new MyConcreteClass().WorkWithAndCreate(action);
        }

        [Test]
        public void CastingDoesNotWorkButThrowsInvalidCastException()
        {
            string aString = CreateWithCast(obj => obj.OnlyInConcreteClass());
            Assert.AreEqual("a string", aString);
        }

        private static string CreateWithCast(Action<MyConcreteClass> action)
        {
            return new MyConcreteClass().WorkWithAndCreate((Action<MyGenericClass<string>>) action);
        }
    }

    internal abstract class MyGenericClass<T>
    {
        public abstract T Create();
        public T WorkWithAndCreate(Action<MyGenericClass<T>> action)
        {
            action(this);
            return this.Create();
        }
    }

    internal class MyConcreteClass : MyGenericClass<string>
    {
        public override string Create()
        {
            return "a string";
        }

        public void OnlyInConcreteClass()
        {
        }
    }
}

Вот пример отформатированного реального мира из моего комментария:

Customer customer = ACustomer(cust =>
        {
            cust.With(new Id(54321));
            cust.With(AnAddress(addr => addr.WithZipCode(22222)));
        });

private static Address AnAddress(Action<AddressBuilder> buildingAction)
{
    return new AddressBuilder().BuildFrom(buildingAction);
}

private static Customer ACustomer(Action<CustomerBuilder> buildingAction)
{
    return new CustomerBuilder().BuildFrom(buildingAction);
}

В нем отсутствуют некоторые детали, но я надеюсь, что это проясняет цель.

Ответы [ 2 ]

3 голосов
/ 29 октября 2010

Причина, по которой работает dynamic, заключается в том, что dynamic не зависит от знания типов объектов во время компиляции.MyGenericClass<string> не имеет метода OnlyInConcreteClass(), но у передаваемого вами экземпляра, конечно, есть метод, и dynamic находит его.

Кстати, вы можете заставить WorkWithAndCreate работать так:

public T WorkWithAndCreate<T1>(Action<T1> action)
    where T1 : MyGenericClass<T>
{
    action((T1)this);
    return this.Create();
}

Тогда вызов также будет работать:

private static string CreateWithCast(Action<MyConcreteClass> action)
{
    return new MyConcreteClass().WorkWithAndCreate(action);
}

Вам больше не нужно его разыгрывать.

А что касается вашего строителя,следующая работа?

private static TResult AnInstance<TBuilder, TResult>(Action<TBuilder> buildingAction)
    where TBuilder : Builder<TResult>, new()
{
    return new TBuilder().BuildFrom(buildingAction);
}
1 голос
/ 29 октября 2010

Это пример того, как использовать динамический:

http://msdn.microsoft.com/en-us/library/dd264736.aspx

Вы сказали:

До сих пор я предполагал, что динамический тип просто «отключает» проверку типов во время компиляции и делает что-то похожее на приведение типов при вызове сообщения в динамическом экземпляре

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

Что касается вашей реальной проблемы, можете ли вы привести более конкретный пример? Возможно, есть лучший дизайн, но вы не сказали нам, что вы пытаетесь сделать - просто то, что вы делаете в настоящее время.

Опасно предположить, что вы можете использовать базу interface и заставить все ваши функции принимать этот базовый интерфейс. Затем поместите методы, которые вы хотите вызвать, в этот интерфейс и внедрите их в ваши конкретные типы. Обычно dynamic используется как обходной вариант, когда у вас нет базового типа или вы не можете изменить базовый тип для добавления виртуальных или абстрактных методов.

Если вы действительно хотите, чтобы это работало как есть, вы должны написать его с параметром универсального типа, а не параметром динамического типа. См. Решение Питера о том, как сделать это правильно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...