2013-01-11 4 views
2

Disons que j'ai une classe - exemple hypothétique:Pourquoi ne puis-je pas instancier une classe générique déduisant des types d'objets anonymes?

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; 
    } 
} 

Bon, classe définie ... et compile, pas de problème.

Donc disons ailleurs dans mon corps de code, je veux lancer cette exception et passer un objet anonyme dans mon exception pour un ou plusieurs des types. Cela devrait garder assez générique que je peux l'utiliser tout lieu où je reçois des réponses inattendues de certains appels dans mon code de base:

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

Avec la plupart des appels anonymes, vous pouvez utiliser l'inférence de type pour que vous ne devez pas définir votre types dans votre appel de méthode ... mais ce morceau de code ne compilera pas. Si je maintiens ma souris sur le "var" de reqData, le compilateur me dit qu'il y a un type défini pour lui, bien qu'un nom impair que personne ne puisse assigner manuellement ... donc théoriquement je pense que le type peut être déduit à partir de ce.

Ma première conclusion est que vous ne pouvez pas passer des types anonymes aux génériques, mais vous pouvez passer en effet des objets anonymes aux génériques d'une certaine façon:

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

Ici « BritishPeople » est un IEnumerable <T> où T est déduit d'être « un

... Je peux itérer sur IEnumerable résultant des objets anonymes et référencer leurs propriétés publiques sans aucun problème ...

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

Bien sûr, je dois utiliser 'var' dans ma boucle parce que nous ne connaissons pas réellement quel type est "BritishExpats".

Je peux donc instancier certains types de classes utilisant des types inférées par des objets anonymes, mais pas d'autres ...

... est là une rime ou la raison de ce qui peut et ne peut pas être instancié, inférée et non inféré par des objets anonymes?

+2

Comment voulez-vous que quelqu'un attrape une exception de type 'InvalidResponseException ' wh En un des paramètres de type générique est un type anonyme? –

+0

@mikez Peut-être qu'il va juste l'attraper en tant qu'exception et remplacer certains membres de sa classe 'InvalidResponseException'. Fait presque aucun sens de toute façon, belle remarque. – Mir

+0

@mikez alors que c'était un exemple inventé, la même chose pourrait être demandée si vous renvoyez votre résultat de votre requête LINQ ... si vous attrapez l'exception InvalidResponseException, il y a de fortes chances que vous sachiez ce qui revient dans l'objet anonyme De la même manière que vous le feriez avec la requête LINQ ... – BenAlabaster

Répondre

2

Vous ne pouvez pas regrouper les génériques, les types anonymes et l'inférence de type dans les constructeurs. Le compilateur ne l'obtiendra tout simplement pas. Vous pouvez créer une solution de contournement par une classe d'aide tels que:

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); 
    } 
} 

Aussi, je ne l'ai pas mentionné, mais votre classe InvalidResponseException ne compilera pas moins fortement modifiées. Le concept de votre question a cependant traversé.

+0

Je suppose que je n'ai pas bien transposé le code hier soir quand j'ai posté ceci, excuses ... J'ai maintenant copié et collé le code de mon prototype qui compile et courir. – BenAlabaster

+0

@BenAlabaster Le code que j'ai écrit, cependant, fonctionne correctement. Est-ce que cela répond à votre question? – Mir

+0

il peut, je suis juste en train de l'analyser ... parmi 1000 autres tâches quotidiennes: D Il semble bon en théorie ... s'il vous plaît tenir;) – BenAlabaster

0

Votre exemple est incorrect. Vous n'avez pas besoin de spécifier des types génériques aux constructeurs. Ils sont déjà connus.

La ligne suivante est invalide, et compiler wont

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

Je pense que vous avez besoin quelque chose de similaire au-dessous

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; 
    } 

} 

Ensuite, vous essayez ci-dessous (pour utiliser l'inférence de type)

throw new InvalidResponseException(exceptionMessage, "Exception Request", "Exception Response"); 
Questions connexes