我的方法中的泛型和类型值显示错误
本文关键字:类型 显示 错误 泛型 方法 我的 | 更新日期: 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
代替T1
和T2
,那么上面的语法工作得很好。如何解决这个泛型方法问题?
如果您希望将T1
和T2
限制为特定的类或接口,则需要使用泛型约束,如下所示:
private List<T2> FillChildControlOnSave<T1, T2>(ref T1 objEntity,
ref List<T1> _entityParent,
ref List<T2> _entityDetail)
where T1 : PinBOMMaster
where T2 : PinBOMDetail
{
...
}
当然PinBOMMaster
和PinBOMDetail
可以用合适的接口替换,例如:
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);
如果你想在T1
和T2
的实例上调用成员,那么你必须告诉编译器关于这些类型的一些信息:
private T2 Bar<T1,T2>(T1 actionable) where T1 : IActionable, T2 : IActionResult
{
T2 result = actionable.Action();
return result;
}
可以在方法参数后使用where
关键字指定对T1
和T2
的约束。
您还可以指定如下内容:
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)
那么你正在访问这一行c
的Detail
属性:
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