المساعد الشخصي الرقمي

مشاهدة النسخة كاملة : Generics, delegates and type inference



C# Programming
10-03-2009, 02:11 AM
Hi all,

I'm trying to use a web service with authenticated access. The service returns a session token with every response and the token may change during the session. Operations on the service all follow a simple pattern: They take one argument representing the request, and return an object representing the response. Each operation has a specific req/response associated with it, but requests are derived from a common base class and responses from another. The base classes provide access to the headers, so they are sufficient to manage the session.

The business object I'm writing that uses the service will execute code in multiple threads. Rather than duplicate the thread-synchronization and token-maintenance code everywhere I invoke a service operation, I thought I'd use generics and delegates so the generic method would implement all the things that should always happen and call the delegate to do the specifics.

Since the service's methods all take a request derived from APIRequest and return a response derived from APIResponse, they conform to Func. Some methods, for example logout, take a request object that contains no information except the request header that identifies the session. I would like to be able to use my generic method, called Invoke, like this:


public void Logout()
{
if (!HasSession) return;
var r = Invoke(globalService.logout);
...
}

void keepAlive()
{
// This runs on a background thread and makes a call to keep the session alive
// if it's close to 20 minutes since the last request.
TimeSpan margin = TimeSpan.FromSeconds(30);

while (HasSession)
{
if (sessionLifeLeft > margin) Thread.Sleep(sessionLifeLeft - margin);
Invoke(globalService.keepAlive);
}
}



I declared the following generic methods to make this possible (the implementation details aren't really important for our purposes here, but I include it as-is since I don't understand why it doesn't work the way I want it to):


protected TResponse Invoke(Func method)
where TRequest : APIRequest, new()
where TResponse : APIResponse
{
return Invoke(method, new TRequest());
}


protected TResponse Invoke(Func method, TRequest req)
where TRequest: APIRequest
where TResponse: APIResponse
{
APIResponse r;
lock (this)
{
req.header = reqHeader;
r = method(req);
watch.Reset();
reqHeader.sessionToken = r.header.sessionToken;
}
return (TResponse)r;
}


I don't understand why I need to cast the return value; the compiler says I can't implicitly convert APIResponse to TResponse. I understand one can't do so in general, but since "method" returns TResponse already I don't understand why such a conversion would result from my code. But with the cast it builds, but when I try to use it as described above the compiler claims the type parameters "cannot be inferred from usage", which I think is weird since there are only two type parameters and both are well-defined from "method", which takes TRequest and returns TResponse.

I have a hunch this has something to do with me thinking in terms of the *formal* parameters rather than the *actual* (run-time) parameters. Still, I'm wondering if there is any way to achieve the desired outcome: to enable invoking the service operations without having to write the type parameters explicitly. It just isn't very elegant and looks rather redundant to code


Invoke(globalService.logout);


Can anyone think of a way to make the type inference work out as I wish, from the Func types?