【问题标题】:'T' does not contain a definition“T”不包含定义
【发布时间】:2016-09-23 11:00:58
【问题描述】:

是否可以执行以下操作(如果可以,我似乎无法使其正常工作..暂时放弃限制)...

如果类型(因为它被省略了)被推断出来,有什么问题?

private void GetGenericTableContent<T>(ref StringBuilder outputTableContent, T item)
{
    outputTableContent.Append("<td>" + item.SpreadsheetLineNumbers + "</td>");
}

// 'item' is either DuplicateSpreadsheetRowModel class or SpreadsheetRowModel class

使用上面的代码,我得到以下错误:

“T”不包含“SpreadsheetLineNumbers”的定义,并且找不到接受“T”类型的第一个参数的扩展方法“SpreadsheetLineNumbers”(您是否缺少 using 指令或程序集引用?)

【问题讨论】:

  • 您需要在泛型方法上添加约束,因为当前 T 的类型是 object 和 object does not contain a definition for 'SpreadsheetLineNumbers' 。如果 DuplicateSpreadsheetRowModel 类或 SpreadsheetRowModel 来自实现相同接口的公共基类,则将其用作约束。
  • C# 泛型不是隐式类型的。您的方法签名的编写方式应该可以调用为GetGenericTableContent(someStringBuilder, 42),使用T = int。显然这会失败,因为 int 没有成员 SpreadsheetLineNumbers 所以 C# 正确地拒绝了你的代码。这就是约束的全部意义所在。
  • 啊,似乎我错误地认为通过省略它在编译时推断的类型,这就是 MS 所说的“编译器可以根据您传入的方法参数推断类型参数;它不能仅从约束或返回值推断类型参数。”和“编译器只包括那些类型推断成功的泛型方法。” ...所以我猜它成功了哪些类型...嗯...如果它有效,我会添加约束。
  • 编译器会在每个调用站点推断通用参数的类型,问题是您的 GetGenericTableContent 方法声称适用于任何类型 T,但它只能适用于具有适当 @ 的类型987654329@ 财产。描述这种类型的方法是将基类或接口约束添加到T,用于定义所需成员的某些类型。

标签: c# generics


【解决方案1】:

没有。泛型类型必须在编译时已知。 想一想,编译器怎么知道T 类型有SpreadsheetLineNumbers 属性保证?如果Tintobject 等原始类型怎么办?

是什么阻止我们调用这样的方法:GetGenericTableContent(ref _, 999) with T as int here?

要修复它,您可以先添加一个包含该属性的接口:

public interface MyInterface 
{
    string SpreadsheetLineNumbers { get; set; }
}

让你的类继承这个接口:

public class MyClass : MyInterface
{
    public string SpreadsheetLineNumbers { get; set; }
}

然后我们使用泛型类型约束让编译器知道类型T 派生自该接口,因此它必须包含并实现其所有成员:

private void GetGenericTableContent<T>(ref StringBuilder outputTableContent, T item) 
    where T : IMyInterface // now compiler knows that type T has implemented SpreadsheetLineNumbers
{
    outputTableContent.Append("<td>" + item.SpreadsheetLineNumbers + "</td>");
}

【讨论】:

    【解决方案2】:

    如果您无法为您的类型(或几种类型之间的通用接口)制作接口:

    private void GetGenericTableContant<T>(ref StringBuilder outputTableContent, T item, Func<T, string> lineNumberAccessor)
    {
         outputTableContent.Append("<td>" + lineNumberAccessor(item) + "</td>");
    }
    

    用法:

    GetGenericTableContent(ref outputTableContent, item, x => x.SpreadsheetLineNumbers);
    

    (或者,如果您的方法中确实不需要项目引用,也可以只传递 SpreadSheetLineNumbers 属性:void GetGenericTableContant&lt;T&gt;(ref StringBuilder outputTableContent, string lineNumbers)

    【讨论】:

      【解决方案3】:

      实际上,如果您知道确定泛型 T 具有确切的属性,则可以对具有 where T : MyClass 的指定类使用 where(泛型类型约束)。


      例如,如果您有两个实体 FooBoo

      class Foo 
      {
          public Guid Id {get; set;}
          public DateTime CreateDate {get; set;}
          public int FooProp {get; set;}
      }
      
      class Boo 
      {
          public Guid Id {get; set;}
          public DateTime CreateDate {get; set;}
          public int BooProp {get; set;}
      }
      

      通过一点重构,我们可以创建一个 BaseClass 来保存公共属性:

      class BaseModel
      {
          public Guid Id {get; set;}
          public DateTime CreateDate {get; set;}        
      }
      

      并将FooBoo修改为:

      class Boo : BaseModel
      {
          public int BooProp {get; set;}
      }
      
      class Foo : BaseModel
      {
          public int FooProp {get; set;}
      }
      

      如果您有一个约束类型为where T : BaseModel 的通用服务,编译器将允许您获取或设置BaseModel 的属性。

      假设您希望将每个实体(FooBoo)添加到 DB,您需要从代码(而不是服务器默认值)设置 CreateDateId 属性:

      public interface IGenericService<T>
      {
          void Insert(T obj);
      }
      
      public class GenericService<T> : IGenericService<T> where T : BaseModel
      {
          public void Insert(T obj)
          {
              obj.Id = Guid.NewGuid();
              obj.CreateDate = DateTime.UtcNow;
              this._repository.Insert(obj);           
          } 
      }
      

      【讨论】:

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