Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"dynamic" keyword with builder pattern hides extension method [duplicate]

I have recently come across a strange behavior with the dynamic keyword whilst I was testing something. This isn't a problem I desperately need solving as I was just experimenting, but I was wondering if anyone could shed any light on what was happening

I have a builder which returns an HttpWebRequest object and an extension method on HttpWebRequest.

One of my builder methods takes a string argument. The whole thing works when I pass the builder method a string, but I pass it a dynamic variable which is a string it no longer works.

It appears as if the builders method which should return type HttpWebRequestBuilder now returns dynamic.

The code below is a simple was to reproduce it and is also available here

Note

To make it work, comment out the line .SetBody(dynamicString) and uncomment the line .SetBody(json).

public class Program
{
    public static void Main()
    {
        dynamic dynamicString = "{ \"test\" : \"value\" }";
        string json = "{ \"test\" : \"value\" }";

        string test = new HttpWebRequestBuilder()
            .SetRequestType()
            //.SetBody(json) //uncomment this and it works
            .SetBody(dynamicString) //uncomment this and it breaks
            .Build()
            .ExtensionMethod();

        Console.WriteLine(test);
    }

}

public class HttpWebRequestBuilder
{
    private readonly HttpWebRequest _request;

    public HttpWebRequestBuilder() 
    {
        Uri uri = new Uri("http://www.google.com");
        _request = WebRequest.CreateHttp(uri);
    }

    public HttpWebRequestBuilder SetRequestType() 
    {
        _request.Method = "POST";
        _request.ContentType = "application/json";

        return this;
    }

    public HttpWebRequestBuilder SetBody(string json) 
    {
        byte[] bytes = Encoding.UTF8.GetBytes(json);
        _request.ContentLength = bytes.Length;

        using (Stream writer = _request.GetRequestStream())
        {
            writer.Write(bytes, 0, bytes.Length);
            writer.Flush();
        }

        return this;
    }

    public HttpWebRequest Build() 
    {
        return _request;
    }
}

public static class WebRequestExtensions 
{
    public static string ExtensionMethod(this HttpWebRequest webRequest) 
    {
        return "extension method worked";
    }
}

I'm guessing this is something strange with the way dynamic objects work. But any explanation would be greatly appreciated.

like image 967
Dreagen Avatar asked Aug 04 '16 13:08

Dreagen


2 Answers

This happens because passing a dynamic parameter makes C# compiler treat the return type of the expression, i.e. .SetBody(dynamicString), as dynamic (relevant explanation of method return types with dynamic parameters).

Extension methods work with dynamic objects only as regular methods, not as extension methods (see Eric Lippert's answer for an explanation of this), hence the compile-time error that you see.

like image 145
Sergey Kalinichenko Avatar answered Oct 19 '22 23:10

Sergey Kalinichenko


Although the answer of dasblinkenlight seems to be right, please remember that you can always avoid this issue by reformulating your code in this way:

public class Program
{        public static void Main()
    {
        dynamic dynamicString = "{ \"test\" : \"value\" }";
        string json = "{ \"test\" : \"value\" }";

        var test = new HttpWebRequestBuilder();
            test.SetRequestType();
            //test.SetBody(json); //still works
            test.SetBody(dynamicString); // works also now
            var b = test.Build();
            var s = b.ExtensionMethod();

        Console.WriteLine(s);
    }
}
like image 28
Zegar Avatar answered Oct 20 '22 00:10

Zegar