1

WCF client classes are typically setup like:

public class Client : ClientBase<IService>, IService

I want to extend these clients with an extension method that is Fluent, so that I can declare a using statement like:

using (new Client().WithCookies(...)) {}

However, I can't figure out a way to maintain the original Type of the caller, without creating some rather clunky calling syntax:

new Client().WithCookies<Client,IService>(...)

I'm not sure why the compiler can't infer the T based on what I've passed in, but it can't, based on the definition of the extension method:

public static T WithCookies<T, TChannel>(this T clientBase, IEnumerable<Cookie> cookies)
   where T : ClientBase<TChannel>, TChannel
   where TChannel : class
{
   HttpRequestMessageProperty requestProperty = new HttpRequestMessageProperty();
   requestProperty.Headers.Add(HttpCookieHeader, string.Join("; ", cookies.Select(c => c.ToCookieString(false))));

   new OperationContext(clientBase.InnerChannel).OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestProperty;
   return clientBase;
}

I know Eric Lippert in his blog, dismisses the notion of specifying a 'I don't care what the type argument to the generic is' (and for generally good reasons) http://blogs.msdn.com/b/ericlippert/archive/2008/05/19/a-generic-constraint-question.aspx

A psuedo-implementation would be something like:

public static T WithCookies<T>(this T clientBase, IEnumerable<Cookie> cookies)
   where T : ClientBase<>
{
   HttpRequestMessageProperty requestProperty = new HttpRequestMessageProperty();
   requestProperty.Headers.Add(HttpCookieHeader, string.Join("; ", cookies.Select(c => c.ToCookieString(false))));

   new OperationContext(clientBase.InnerChannel).OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestProperty;
   return clientBase;
}

This is one scenario where it seems to me a good fit, as I don't care what TChannel is -- I'm not going to use it in my code, I just want to use any ClientBase<> as a constraint.

With that said, can anyone come up with a creative way to implement my Fluent requirement without the specification of the Types?

Keep in mind that if you don't return the original item passed in, you lose the ability to call service methods implemented in IService.

Ethan J. Brown
  • 2,308
  • 3
  • 20
  • 27

3 Answers3

1

This was answered in another thread by Eric Lippert -

Generic extension method : Type argument cannot be inferred from the usage

The short answer is, compiler inference doesn't work like that unfortunately.

More details on his blog: http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx

Community
  • 1
  • 1
Ethan J. Brown
  • 2,308
  • 3
  • 20
  • 27
  • IanG also has some good info here (relative to the spec): http://social.msdn.microsoft.com/Forums/en/csharplanguage/thread/a4847737-4a6b-4fcd-89f2-1b213aaf8422 – Ethan J. Brown Jan 18 '11 at 20:22
0

Not an answer to the question, but something to be aware of if you manage to get this working...

If you use the using (new Client().WithCookies(...)) syntax and your WithCookies method happens to throw an exception then the Client instance won't be diposed.

LukeH
  • 263,068
  • 57
  • 365
  • 409
  • Right -- as discussed here btw -- http://ayende.com/Blog/archive/2009/01/15/avoid-object-initializers-amp-the-using-statement.aspx – Ethan J. Brown Jan 07 '11 at 19:44
-1

The way to implement your pseudo-implementation would be via reflection. You could do this:

  1. Use reflection to look at clientBase and work out what TChannel is (by going up the type hierarchy and looking for ClientBase<TChannel>)
  2. Obtain a specialized definition of WithCookies using typeof(YourClass).GetMethod("WithCookiesImpl").MakeGenericMethod(new[] { typeof(T), channelType })
  3. Invoke this specialized definition of WithCookies

For me the question is, why can't the compiler infer the types in the call to T WithCookies<T, TChannel>? It has the T : ClientBase<TChannel> constraint on it; since T can only inherit from ClientBase once, there can only be one TChannel. (If ClientBase had been an interface then there could have been more than one TChannel.)

Tim Robinson
  • 53,480
  • 10
  • 121
  • 138
  • That won't buy me anything in this context. Then there's no generic constraint on the method and I can apply .WithCookies() to instances where it doesn't belong. What TChannel is is basically irrelevant for my needs. Any ClientBase<> is acceptable. – Ethan J. Brown Jan 07 '11 at 19:48
  • And I totally agree that the issue is why the compiler can't infer -- I'm at a loss. Where's Eric Lippert when you need him? – Ethan J. Brown Jan 07 '11 at 19:49