如何使用 Linq 和 XML 实现泛型方法方法
本文关键字:实现 泛型方法 方法 XML 何使用 Linq | 更新日期: 2023-09-27 18:36:43
好吧,我想我有一些重复的代码可以适合使用泛型。
我有两个不同的 Xml 文件,我打开、查询并作为绑定到 GridView 的集合返回。 这些集合是用 xml 中的数据填充的自定义类的列表。 每个网格视图都有其相应的自定义类。 目前我有两个,并说这些类的名称是XmlDataSource1和XmlDataSource2。
下面是使用 XmlDataSource1 作为示例的当前工作示例。请注意,XmlDataSource1 对象的构造函数从查询中获取 XElements 并填充自身。没什么疯狂的。
GridView gv = new GridView();
gv.DataSource = GetXmlDataSource1(pathToXmlFile);
gv.DataBind();
public List<XmlDataSource1> GetXmlDataSource1(string pathToXmlFile)
{
XDocument xml = XDocument.Load(pathToXmlFile);
IEnumerable<XmlDataSource1> query = from s in xml.Descendants("NodeForXml1")
select new XmlDataSource1(s);
// Where clauses based on user inputs (deferred execution)
query = query.Where(x => x.ID = SomeUserInputId);
// More of these where clauses if they have inputs for them...
// Convert to a List and return
return query.ToList();
}
现在,要实现 GetXmlDataSource2() 方法,它大约 98% 相同。 当然,主要区别在于 linq 查询的选择部分创建 XmlDataSource2 对象的新实例、"NodeForXml2"后代目标节点,以及一些适用/不适用的 where 子句。
如何使这些 GetXmlDataSource# 方法成为通用方法? 理想情况下,我想按如下所述调用它,这是我尝试过的,但我无法让 linq 查询的选择部分调用正确数据对象的构造函数。
GridView gv1 = new GridView();
GridView gv2 = new GridView();
gv1.DataSource = GetXmlDataSource<XmlDataSource1>(pathToXmlFile);
gv2.DataSource = GetXmlDataSource<XmlDataSource2>(pathToXmlFile);
gv1.DataBind();
gv2.DataBind();
public List<T> GetXmlDataSource<T>(string pathToXmlFile)
{
// The type of T in case I need it
Type typeOfT = typeof(T);
XDocument xml = XDocument.Load(pathToXmlFile);
// How to make new XmlDataSource1 and 2 objects?? This statement doesn't work.
IEnumerable<T> query = from s in xml.Descendants("NodeForXml1")
select new T(s);
// How to return the IEnumerable query to a List of the T's?
return query.ToList();
}
我离我有多远? 我接近了吗?
一种解决方案可能是使用 Activator.CreateInstance
IEnumerable<T> query = from s in xml.Descendants("NodeForXml1")
select (T)Activator.CreateInstance(typeOfT, s);
但是要注意性能问题,这是Jon Skeet处理它的一篇很棒的帖子:
显然,这比召集代表要慢 - 可能是由于 尝试找到具有反射的可访问构造函数,并调用 它
因此,如果需要性能,最好将委托传递给GetXmlDataSource1
方法,并使用它来在 Linq 查询中创建所需的实例。
关于您需要将 XmlDataSources 公共属性访问到GetXmlDataSource<T>
方法中,您至少有 2 种解决方案:
1:创建一个包含公共属性的接口:
public interface IXmlDataSource
{
string ID { get; set; }
string CommonProperty1 { get; set; }
string CommonProperty2 { get; set; }
}
这个将由你的XmlDataSources实现。这是一个典型的实现:
public class XmlDataSource1 : IXmlDataSource
{
public string ID { get; set; }
public string CommonProperty1 { get; set }
public string CommonProperty2 { get; set }
... // the rest of your code
}
最后,约束T
类型将授予您在需要这些属性时访问这些属性的权限query = query.Where(x => x.ID = SomeUserInputId);
public List<T> GetXmlDataSource<T>(string pathToXmlFile) where T : IXmlDataSource
2 : 委托也可以做到这一点,这是在这种情况下的典型调用:
GetXmlDataSource<XmlDataSource1>(pathToXmlFile, (query, result) =>
{
return query.Select(e => new XmlDataSource1(e)).Where(x => x.YourProperty == value);
});
使用如下所示的GetXmlDataSource<T>
方法签名:
public List<T> GetXmlDataSource<T>(string pathToXmlFile, Func<IEnumerable<XElement>, IEnumerable<T>> transform)
{
// The type of T in case I need it
Type typeOfT = typeof(T);
XDocument xml = XDocument.Load(pathToXmlFile);
IEnumerable<XElement> query = from s in xml.Descendants("NodeForXml1")
select s;
// Create and filter XmlDataSource1 instances thanks to the "transform" delegate
return transform(query).ToList();
}
看起来你已经很接近了 - 一种选择是让调用者传入函数来创建实例:
public List<T> GetXmlDataSource<T>(string pathToXmlFile,
string elementName,
Func<XElement,T> factoryMethod)
{
...
IEnumerable<T> query = from s in xml.Descendants(elementName)
select factoryMethod(s);
...
}
然后来电者会说:
List<XmlDataSource1> list = GetXmlDataSource1(pathToXmlFile,
"NodeForXml1",
s => new XmlDataSource1(s))