我的方法中的泛型和类型值显示错误

本文关键字:类型 显示 错误 泛型 方法 我的 | 更新日期: 2023-09-27 18:09:41

我想写一个泛型方法,下面的代码给出了错误。

不能将类型' T2 '转换为' T1 '

' T1 '不包含'Action'的定义,也没有扩展方法"Action"接受类型为"T1"的第一个参数可以是找到(您是否缺少using指令或程序集引用?)

private List<T2> FillChildControlOnSave<T1, T2>(
        ref T1 objEntity, ref List<T1> _entityParent, ref List<T2> _entityDetail)
{
    foreach (T2 c in _entityDetail)
    {
        if (c.Action == XERP.Entity.ActionMode.Add)            
            objEntity.PlnBOMDetails.Add(c);
        var tmp = objEntity.PlnBOMDetails
                     .Where(p => p.BOMDetailRecordID == c.BOMDetailRecordID && 
                                 p.BOMID == c.BOMID && 
                                 p.IsDeleted == false)
                     .FirstOrDefault();
        if (tmp != null)
           if (c.Action == Entity.ActionMode.Delete)            
               objController.Context.PlnBOMDetails.DeleteObject(tmp);            
    }
    return _entityDetail;
}

如果我用PlnBOMMaster,PlnBOMDetail代替T1T2,那么上面的语法工作得很好。如何解决这个泛型方法问题?

我的方法中的泛型和类型值显示错误

如果您希望将T1T2限制为特定的类或接口,则需要使用泛型约束,如下所示:

private List<T2> FillChildControlOnSave<T1, T2>(ref T1 objEntity, 
                                                ref List<T1> _entityParent, 
                                                ref List<T2> _entityDetail)
    where T1 : PinBOMMaster
    where T2 : PinBOMDetail
{
    ...
}

当然PinBOMMasterPinBOMDetail可以用合适的接口替换,例如:

public interface IMaster<TDetail>
    where TDetail : IDetail
{
    List<TDetail> Details { get; }
}
public interface IDetail
{
    int RecordID { get; }
    int BOMID { get; }
    bool isDeleted { get; }
    Entity.ActionMode Action { get; set; }
}
public class PinBOMMaster : IMaster<PinBOMDetail>
{
    ...
}
public class PinBOMDetail : IDetail
{
    ...
}
private List<T2> FillChildControlOnSave<T1, T2>(ref T1 objEntity, 
                                                ref List<T1> _entityParent, 
                                                ref List<T2> _entityDetail)
    where T1 : IMaster<T2>
    where T2 : IDetail
{
    ...
}

注意:如果你的实体是由代码生成工具创建的,你将不得不使用部分类来应用接口实现。

当然,你可能不能使用objController.Context.PlnBOMDetails.Add(c)。您必须将其替换为泛型代码,如下所示:

// for DbContext
objController.Context.Set<T2>().Add(c);
// for ObjectContext
objController.Context.CreateObjectSet<T2>().AddObject(c);

当然,您也可以编写自己的方法来完成此操作。例如,IDetail/IMaster接口可以有一个AddToContext(...)方法,该方法接受上下文并将自身插入到适当的集合中。然后在FillChildControlOnSave中调用c.AddToContext(objConctroller.Context);

如果你想在T1T2的实例上调用成员,那么你必须告诉编译器关于这些类型的一些信息:

private T2 Bar<T1,T2>(T1 actionable) where T1 : IActionable, T2 : IActionResult
{
    T2 result = actionable.Action();
    return result;
}

可以在方法参数后使用where关键字指定对T1T2的约束。

您还可以指定如下内容:

where T : new()  // has a default constructor
where T : struct // is a value type
where T : class  // is a reference type

您正在使用以下行循环遍历T2列表:

foreach (T2 c in _entityDetail)

那么你正在访问这一行cDetail属性:

if (c.Action == XERP.Entity.ActionMode.Add)

编译器如何知道类型T2包含这样的属性?

你需要将泛型约束为一种接口,像这样:

interface IPlnBOMDetail { XERP.Entity.ActionMode Action {get;}}
class PlnBOMDetail : IPlnBOMDetail {}
private List<T2> FillChildControlOnSave<T1, T2>(ref T1 objEntity, ref List<T1> _entityParent, ref List<T2> _entityDetail)
    where T2 : IPlnBOMDetail 

其他代码都是一样的

旁注:使用ref参数是一个代码气味。
我建议你阅读这个主题:在c#中何时使用ref,何时不需要。
DR: Jon Skeet说

几乎不需要使用ref/out