开发者

C# constraint generic parameter to a T<T2> for Fluent interface / compiler can't infer types

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(...)) {}

Howev开发者_JAVA技巧er, 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.


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


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.


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.)

0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜