19

I have the following code which creates a dynamic object that is assigned to the smtpClient variable.

public class TranferManager
{
    public void Tranfer(Account from, Account to, Money amount)
    {
        // Perform the required actions
        var smtpClient = New.SmtpClient();
        smtpClient.Send("info@bank.com", "from.Email", "Tranfer", "?");
        // In the previous line I get a Microsoft.CSharp.RuntimeBinder.RuntimeBinderException
        // with the description = "'object' does not contain a definition for 'Send'"
    }
}

public static class New
{
    public static dynamic SmtpClient(params object[] parameters)
    {
        return typeof(SmtpClient).New(parameters);
    }
}

public static class CreationExtensions
{
    private static Dictionary<Type, Func<object, dynamic>> builders =
        new Dictionary<Type, Func<object, dynamic>>();

    public static dynamic New(this Type type, params object[] parameters)
    {
        if(builders.ContainsKey(type))
            return builders[type](parameters);

        return Activator.CreateInstance(type, parameters);
    }

    public static void RegisterBuilder(this Type type, Func<object, dynamic> builder)
    {
        builders.Add(type, builder);
    }
}

To test it I am using the UT (below):

    [TestMethod()]
    public void TranferTest()
    {
        typeof(SmtpClient).RegisterBuilder(p => 
            new
            {
                Send = new Action<string, string, string, string>(
                (from, to, subject, body) => { })
            }
        );

        var tm = new TranferManager();
        tm.Tranfer(new Account(), new Account(), new Money());
        // Assert
    }

When I, using the inmediate windows, ask for the smtpClient type I get:

smtpClient.GetType()
{<>f__AnonymousType0`1[System.Action`4[System.String,System.String,System.String,System.String]]}

And when I ask for its members I get:

smtpClient.GetType().GetMembers()
{System.Reflection.MemberInfo[7]}
    [0]: {System.Action`4[System.String,System.String,System.String,System.String] get_Send()}
    [1]: {System.String ToString()}
    [2]: {Boolean Equals(System.Object)}
    [3]: {Int32 GetHashCode()}
    [4]: {System.Type GetType()}
    [5]: {Void .ctor(System.Action`4[System.String,System.String,System.String,System.String])}
    [6]: {System.Action`4[System.String,System.String,System.String,System.String] Send}

So, my question is: Why am I getting that exception?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
lontivero
  • 5,235
  • 5
  • 25
  • 42
  • uhmm... copying all your code in a Console Application (and the content of your test method in my "main" method) it does not throw any exception for me. I guess your TestMethod is in a different dll. Maybe there's an issue with the .NET version you're targeting in the dll or its references? – Paolo Falabella Apr 15 '11 at 17:30
  • Same thing here. When I run your code (having provided suitable definitions of Account, Money, etc) I get no exception. Can you provide a small program that *actually compiles, runs, and demonstrates the problem?* – Eric Lippert Apr 15 '11 at 17:35
  • You are right, copying the test code in a "Main()" method of a console application it works. Now, I can see the problem: the annonymous type (created in the test dll) is not visible out of its dll. – lontivero Apr 15 '11 at 18:21

2 Answers2

37

Anonymous types are internal, if you cross assembly boundaries dynamic can't resolve the property.

Rather than using an anonymous type, try using an actual type or an Expando Object.

jbtule
  • 31,383
  • 12
  • 95
  • 128
  • Thanks, The reason is right and pin point, can you give any soln. – Aditya Acharya Sep 12 '11 at 12:23
  • 2
    Changing the internals to be visible to another assembly it's also a nice solution for unit tests - http://cstruter.com/blog/278 – Felipe Castro Nov 16 '12 at 21:18
  • Please vote for this feature in Visual Studio [UserVoice](https://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/7062098-support-object-initializers-on-expandoobjects). – orad Feb 06 '15 at 20:32
11

In the AssemblyInfo.cs try to add following:

[assembly: InternalsVisibleTo("NameSpace1.SubNameSpace1")]

where NamsSpace1 is your project name and SubNameSpace is namespace of your dynamic/anonym object

user1195202
  • 1,423
  • 1
  • 16
  • 20