Question

Let's say I have some class - hypothetical example:

public class InvalidResponseException<TReq, TResp> : Exception
{
    public TReq RequestData { get; protected set; }
    public TResp ResponseData { get; protected set; }

    public InvalidResponseException(string message, TReq requestData, TResp responseData)
        : this(message, null, requestData, responseData)
    {
    }

    public InvalidResponseException(string message, Exception innerException, TReq requestData, TResp responseData)
        : base(message, innerException)
    {
        RequestData = requestData;
        ResponseData = responseData;
    }
}

Okay, class defined... and compiles, no problem.

So lets say somewhere else in my body of code, I want to throw this exception and pass an anonymous object into my exception for one or more of the types. This should keep it generic enough that I can use it any place where I get unexpected responses from some call in my codebase:

var reqData = new { 
    Context = SomeDataContext,
    Username = SomeUserName,
    ParentID = 54,
    RequestValues = ListOfItemsToGet
}

var respData = results.ToList();

string exceptionMessage = string.Format("Invalid response data detected. Requested {0} items received {1}.", ListOfItemsToGet.Count(), results.Count());

throw new InvalidResponseException(exceptionMessage, reqData, respData);

With most anonymous calls you can use type inference so you don't have to define your types in your method call... but this piece of code won't compile.

If I hold my mouse over the "var" of reqData, the compiler tells me that there's a type defined for it, albeit an odd name nobody could manually assign... so theoretically I would think the type can be inferred from that.

My initial conclusion was that you can't pass anonymous types to generics, but then you can indeed pass anonymous objects to generics in some fashion:

var BritishExpats = from p in People
                    where p.CountryOfBirth == "United Kingdom" && p.CountryOfResidents != p.CountryOfBirth
                    select new { FirstName = p.FirstName, LastName = p.LastName }

Here "BritishPeople" is an IEnumerable<T> where T is inferred to be 'a

... I can iterate over the resulting IEnumerable of anonymous objects and reference their public properties without any issues...

foreach (var ExPat in BritishExpats)
{
    Console.WriteLine("{0}, {1}", Expat.LastName, Expat.FirstName);
}

Of course I have to use 'var' in my loop because we don't really know what type "BritishExpats" is.

So I can instantiate some types of classes using types inferred by anonymous objects, but not others...

...is there a rhyme or reason as to what can and cannot be instantiated, inferred and not inferred by anonymous objects?

Was it helpful?

Solution

You can't put together generics, anonymous types and type inference in constructors. The compiler just won't get it. You can create a workaround through a helper class such as:

public static class InvalidResponseExceptionHelper
{
    public static InvalidResponseException<TReq, TResp> Create<TReq, TResp>
        (string message, TReq requestData, TResp responseData)
    {
        return new InvalidResponseException<TReq, TResp>(message, 
            requestData, responseData);
    }
}

Also, I didn't mention it, but your InvalidResponseException class won't compile unless heavily modified. The concept of your question went through, however.

OTHER TIPS

Your example is incorrect. You donot need to specify generic types to constructors. They are already known.

The following line is invalid, and wont compile

public InvalidResponseException<TReq, TResp>(string message) // TReq,TResp are already known to constructor

I think you need something similar to below

public  class InvalidResponseException<TReq, TResp> : Exception
{
    public TReq RequestData { get; protected set; }
    public TResp ResponseData { get; protected set; }

    public InvalidResponseException (string message):
        this(message, default(TReq), default(TResp))    
    {
    }
    public InvalidResponseException (string message, TReq requestData, TResp responseData)  : base(message)
    {
        RequestData = requestData;
        ResponseData = responseData;
    }

}

Then you try below (to make use of type inference)

  throw new InvalidResponseException(exceptionMessage, "Exception Request", "Exception Response");
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top