【问题标题】:Why can't I instantiate a generic class inferring types from anonymous objects?为什么我不能实例化从匿名对象推断类型的泛型类?
【发布时间】:2013-01-11 04:26:28
【问题描述】:

假设我有一些课程 - 假设示例:

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

好的,类已定义...并编译,没问题。

所以让我们在我的代码体的其他地方说,我想抛出这个异常并将一个匿名对象传递给我的一个或多个类型的异常。这应该使它足够通用,以便我可以在我的代码库中的某些调用得到意外响应的任何地方使用它:

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

对于大多数匿名调用,您可以使用类型推断,因此您不必在方法调用中定义您的类型...但是这段代码不会编译。

如果我将鼠标悬停在 reqData 的“var”上,编译器会告诉我为它定义了一个类型,尽管没有人可以手动分配一个奇怪的名称......所以理论上我认为可以从那。

我最初的结论是你不能将匿名类型传递给泛型,但是你确实可以以某种方式将匿名对象传递给泛型:

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

这里的“BritishPeople”是一个 IEnumerable,其中 T 被推断为 'a

...我可以遍历生成的匿名对象的 IEnumerable 并毫无问题地引用它们的公共属性...

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

当然,我必须在循环中使用“var”,因为我们真的不知道“BritishExpats”是什么类型。

所以我可以使用匿名对象推断的类型来实例化某些类型的类,但不能实例化其他类型的类...

...对于匿名对象可以实例化、推断和不推断什么可以和不可以有押韵或理由吗?

【问题讨论】:

  • 当泛型类型参数之一是匿名类型时,您如何期望某人捕获InvalidResponseException&lt;TReq, TResp&gt; 类型的异常?
  • @mikez 也许他会把它当作一个Exception 并在他的InvalidResponseException 类中覆盖它的一些成员。无论哪种方式都几乎没有意义,好话。
  • @mikez 虽然这是一个人为的例子,但如果您从 LINQ 查询返回结果,可能会问同样的事情......如果您遇到 InvalidResponseException,您很可能知道接下来会发生什么以与 LINQ 查询相同的方式返回匿名对象...

标签: c# instantiation type-inference anonymous-objects


【解决方案1】:

您不能在构造函数中将泛型、匿名类型和类型推断放在一起。编译器就是不会得到它。您可以通过帮助类创建解决方法,例如:

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

另外,我没有提到它,但你的 InvalidResponseException 类不会编译,除非进行大量修改。但是,您的问题的概念通过了。

【讨论】:

  • 我想我昨晚发布此代码时没有很好地转置代码,抱歉...我现在已经从我的原型中剪切并粘贴了可以编译和运行的代码。
  • @BenAlabaster 不过,我编写的代码可以正常工作。它回答了你的问题吗?
  • 可能,我只是在分析它...在其他 1,000 项日常任务中:D 理论上看起来不错...请稍等;)
【解决方案2】:

您的示例不正确。您不需要为构造函数指定泛型类型。他们是众所周知的。

以下行无效,无法编译

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

我认为你需要类似下面的东西

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

}

然后你试试下面(利用类型推断)

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多