0

我正在尝试编写一个通用方法,下面的代码给出了错误。

无法将类型“ T2”转换为“ T1

' T1' 不包含 'Action' 的定义,并且找不到接受类型 ' T1' 的第一个参数的扩展方法 'Action'(您是否缺少 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;

}

如果我用then 替换上面的语法就可以了T1。如何解决这个泛型方法问题?T2PlnBOMMaster,PlnBOMDetail

4

3 回答 3

2

如果你想限制某些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);.

于 2013-04-18T07:33:06.680 回答
2

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

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

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

您还可以指定以下内容:

where T : new()  // has a default constructor
where T : struct // is a value type
where T : class  // is a reference type
于 2013-04-18T07:34:26.047 回答
1

您正在循环T2使用此行的列表:

foreach (T2 c in _entityDetail)

然后您正在访问此行中的Detail属性c

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 以及何时不需要
TL;DR : Jon Skeet

你几乎不需要使用 ref/out

于 2013-04-18T07:35:41.193 回答