7

I got a webservice that accepts multiple calls that require different handling and validation, using generics I've managed to create a common class that accepts a handler and a validator and it looks like this

    public class PetitionService<T1, T2>
    where T1 : Headers, Petition
    where T2 : Headers
{
    PetitionHandler<T1, T2> petitionHandler;
    PetitionValidator<T1> petitionValidator;

    public PetitionService(PetitionHandler<T1, T2> handler, PetitionValidator<T1> validator)
    {
        if (handler == null)
        { throw new ArgumentNullException("petitionHandler cannot be null"); }
        if (validator == null)
        { throw new ArgumentNullException("petitionValidator cannot be null"); }

        petitionHandler = handler;
        petitionValidator = validator;
    }

    public T2 ProcessPetition(T1 petition)
    {
        petitionValidator.Validate(petition);
        return petitionHandler.Handle(petition);
    }
}

That's all fine and dandy, and it's working just fine, but I wanted to add another layer on top of it. Either a factory class or a message hub where the service class is mapped by the message type id. The problem with that is that since each handler return a different child of Headers it'll lead to the caller needing to unbox the response. Is there a way to avoid unboxing, is unboxing not so bad or am I just overdoing it?.

  • 1
    Just a suggested edit to help people understand your question when they see it in a list. You're obviously speaking some dialect of Latin, but in English "petition" isn't appropriate here: it has a much more specific meaning. If you change your title to "Generic request processing" then more people will know what you are asking about. There is no need to change "Petition" in your code. – Martin Kochanski Jun 8 '16 at 11:25
  • 1
    Don't you mean "cast the response"? I don't see any boxing going on here. – Frank Hileman Jun 9 '16 at 16:28
  • Well, I'd need unboxing and I guess that's what you mean by "cast the response". Let's say I got a CreateUserResponse that inherits from Headers, if Header is the return type of PetitionService I'd need to unbox the response. – Zalomon Jun 10 '16 at 6:26
  • Could you explain this some more? What is being unboxed exactly? Which types here are value types? And maybe some example code to demonstrate your last paragraph, too. – Ben Aaronson Jul 19 '17 at 14:54
0

I wanted to add another layer on top of it. Either a factory class or a message hub where the service class is mapped by the message type id. The problem with that is that since each handler return a different child of Headers it'll lead to the caller needing to unbox the response. I

OP, if I understand your question (I have to read between the lines), you want a factory that chooses what type of PetitionService to return by choosing from a lookup table. That table would have a dictionary key of Message ID and the value would be a base type of some kind that is common to all your various petition services. Because the dictionary contains the base type, you are worried about casting each dictionary entry to its more derived type in order to access the specific properties of the headers and petitions.

(This is not boxing or unboxing... that has to do with passing value types where an object is needed).

The short answer is-- yes, you are overthinking it. It is not a big deal to cast from a base type to a derived type, it is pretty common in object oriented programming.

The longer answer is-- yes, you CAN avoid the cast, using contravarience, but it is pretty complicated (for me, anyway). I answered a similar question for another poster a few weeks ago. It took me several tries to get it right. If you'd like to check it out, read this answer, the one entitled "fourth time is the charm."

Maybe some kind poster who is better with generics than I can provide you with a simpler example.

0

Your question and concern are confusing.

I'm over-simplifying, but in C#, generic type constraints such as on your T1 and T2 are generally of the form "base-class, interface1, interface2..." or "interface1, interface2..." or "struct, interface1, interface2..."

Constraints on Type Parameters

Because structs/value types cannot be derived, I assume then that your "where : ..." constraints on Headers and Petition are there about interfaces -- if only because of the "where : Headers, Petition" and you mentioned that there would be "... a different child of Headers".

A more common naming convention, btw, consists in starting interface type names with "I" -- so, "IHeaders" and "IPetition" would help the reader, imo.

Now as far as boxing/unboxing goes, as it stands in your code (and as Frank said), there is nothing in there that will require the compiler (yet) to worry about it.

Things would be different if you had somewhere, say, a

DoSomething(object value)

and then you'd have a call site like,

public T2 ProcessPetition(T1 petition)
{
    DoSomething(petition);
    // etc
}

then, yes, depending on whether T1 gets closed over by a class (reference) type, say

class PetitionObject : IHeaders, IPetition { ... }

var service = new PetitionService<PetitionObject, ...>(...)
// ...
service.ProcessPetition(new PetitionObject(...));

vs. a struct/value type, say

struct PetitionDatum : IHeaders, IPetition { ... }

var service = new PetitionService<PetitionDatum, ...>(...)
// ...
service.ProcessPetition(new PetitionDatum(...));

boxing would need to occur at the DoSomething() call site in ProcessPetition(...), in the latter case -- because of the

object value

formal argument and because PetitionDatum is a value type, precisely (so that "value" can become a reference to a then-boxed copy of the PetitionDatum instance given by the caller when DoSomething()'s body executes).

You may be confusing some other concept with what "boxing" is commonly referred to.

'HTH,

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.