开发者

Why does dynamic type work where casting does not?

开发者 https://www.devze.com 2023-01-22 06:58 出处:网络
My guess until now was that a dynamic type just \"switches off\" type checking during compilation and does something similar to a type cast when a message is invoked on a dynamic instance. Obviously s

My guess until now was that a dynamic type just "switches off" type checking during compilation and does something similar to a type cast when a message is invoked on a dynamic instance. Obviously something else is going on.

The attached NUnit test case shows my problem: Using a dynamic type I can use a method only available in the concrete subclass, but I cannot do the same using a cast (results in InvalidCastException). I'd rather do the casting since this gives me full code completion in VS.

Can anyone explain what's happening and/or give me a hint how I could get code completion in my case without having to reimplement the WorkWithAndCreate-method in every concrete subclass?

cheers, Johannes

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开发者_如何学Python)
        {
            action(this);
            return this.Create();
        }
    }

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

        public void OnlyInConcreteClass()
        {
        }
    }
}

Here's the formatted real world example from my comment:

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);
}

Some details are missing from it but I hope it makes the purpose clear.


The reason dynamic works is that dynamic does not depend on compile time knowledge of the object types. MyGenericClass<string> does not have the method OnlyInConcreteClass(), but the instance you are passing of course does have the method and dynamic finds this.

By the way, you can make WorkWithAndCreate work like this:

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

Then, the call will work too:

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

You now don't have to cast it anymore.

And concerning your builder, would the following work?

private static TResult AnInstance<TBuilder, TResult>(Action<TBuilder> buildingAction)
    where TBuilder : Builder<TResult>, new()
{
    return new TBuilder().BuildFrom(buildingAction);
}


This is an example of how to use dynamic:

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

You said:

My guess until now was that a dynamic type just "switches off" type checking during compilation and does something similar to a type cast when a message is invoked on a dynamic instance

Actually, it uses reflections to look up the methods, properties, and fields you invoke by name, at runtime. No casting is done, unless you actually cast the object back to its underlying type.

As for your actual problem, can you give a more concrete example? There may be a better design, but you haven't told us what you're trying to do - just what you're currently doing.

Hazarding a guess, you may want to use a base interface, and make all your functions accept that base interface. Then put the methods you want to call on that interface, and implement them in your concrete types. Usually dynamic is used as a work around when you don't have a base type, or can't modify the base type to add virtual or abstract methods.

If you really want to get this working, as-is, you must write it with a generic type parameter, not a dynamic type parameter. See Pieter's solution for how to do this properly.

0

精彩评论

暂无评论...
验证码 换一张
取 消