使包装器 class 的扩展 Methods/Constructors 通用
Making Extension Methods/Constructors of a wrapper class Generic
在一项服务中(我无法更改)我有两个对象 classes Bar
和 Baz
具有大部分相似的属性( 但遗憾的是不,它们不是从相同的基础派生而来 class 或从相同的接口继承...是的——愚蠢 ),以及依赖性 class 与其相关的 BarQux
和 BazQux
属性相关:
public class Bar public class Baz
{ {
public int ID { get; set; } public int ID { get; set; }
public bool Active { get; set; } public bool Active { get; set; }
public int BarQux { get; set; } public int BazQux { get; set; }
... ...
} }
public class Qux
{
public int ID { get; set; } // Corresponds to BarQux and BazQux
public string Name { get; set; }
}
在 WPF 屏幕中,我将每种类型的列表(Baz
和 Bar
)绑定到两个单独的 ListView。我需要每个人都有一个额外的 Selected
CheckBox 列。为此,我创建了一个具有公共属性的包装器 class、附加的 Selected
属性 以及每个属性的构造函数:
public class Foo
{
public Foo(Bar bar, Qux qux)
{
this.Active = bar.Active;
this.FooQux = string.Format("{0} - {1}", qux.ID, qux.Name);
...
}
public Foo(Baz baz, Qux qux)
{
this.Active = baz.Active;
this.FooQux = string.Format("{0} - {1}", qux.ID, qux.Name);
...
}
public bool Selected { get; set; }
public int ID { get; set; }
public bool Active { get; set; }
public string FooQux { get; set; }
...
}
为了将 class Baz
和 Bar
的每个集合转换为 Foo
的集合,我创建了以下扩展方法:
public static List<Foo> ToFoo(this IEnumerable<Bar> bars, IEnumerable<Qux> quxs)
{
List<Foo> foos = new List<Foo>();
foreach (Bar bar in bars)
{
Foo foo = new Foo(bar, quxs.Single(qux => qux.ID == bar.BarQux));
foos.Add(foo);
}
return foos;
}
public static List<Foo> ToFoo(this IEnumerable<Baz> bazs, IEnumerable<Qux> quxs)
{
List<Foo> foos = new List<Foo>();
foreach (Baz baz in bazs)
{
Foo foo = new Foo(baz, quxs.Single(qux => qux.ID == baz.BazQux));
foos.Add(foo);
}
return foos;
}
问题:
如何使它通用?
理论、实现和错误:
因为除了 Bar
和 Baz
参数之外,构造函数实际上是相同的,我可以以某种方式使用泛型类型 T
来制作一个构造函数并仍然抓取属性?
public class Foo<T>
{
public Foo(T obj, Qux qux)
{
this.Active = obj.Active; // 'T' does not contain a definition for 'Active'...
this.Qux = string.Format("{0} - {1}", qux.ID, qux.Name);
...
}
...
}
更改构造函数以接收整个 Qux
对象集合并在那里执行 quxs.Single(qux => qux.ID == object.ObjectQux)
逻辑。然后将扩展方法变成一个泛型方法,如下所示。
public static List<Foo> ToFoo<T>(this IEnumerable<T> objCollection, IEnumerable<Qux> quxs)
{
List<Foo> foos = new List<Foo>();
foreach (T obj in objCollection)
{
Foo foo = new Foo(obj, quxs); // The best overloaded method... has some invalid arguments.
foos.Add(foo);
}
return foos;
}
1和2合并?有什么我没有想到的吗?
如果您的属性有限并且列表中的项目数量也很少,那么您可以使用Reflection
。由于您将在 WPF 中使用它,我还建议将该进程移动到单独的后台线程。
通用 Foo
public class Foo<T>
{
public Foo(T obj, Qux qux)
{
//this.Active = obj.Active;
var fooProps = GetType().GetProperties().ToList();
var tProps = typeof(T).GetProperties()
.Where(p =>
{
var w = fooProps.FirstOrDefault(f => f.Name == p.Name);
return w != null;
}).ToList();
foreach (var propertyInfo in tProps)
{
var val = propertyInfo.GetValue(obj);
fooProps.First(e => e.Name == propertyInfo.Name).SetValue(this, val);
}
this.FooQux = string.Format("{0} - {1}", qux.ID, qux.Name);
}
public bool Selected { get; set; }
public int ID { get; set; }
public bool Active { get; set; }
public string FooQux { get; set; }
}
您的扩展方法
public static IEnumerable<Foo<Bar>> ToFoo(this IEnumerable<Bar> bars, IEnumerable<Qux> quxs)
{
return bars.
Select(bar => new Foo<Bar>(bar, quxs.Single(qux => qux.ID == bar.BarQux))).ToList();
}
public static IEnumerable<Foo<Baz>> ToFoo(this IEnumerable<Baz> bazs, IEnumerable<Qux> quxs)
{
return bazs.
Select(baz => new Foo<Baz>(baz, quxs.Single(qux => qux.ID == baz.BazQux))).ToList();
}
public static IEnumerable<Qux> ToQuxes(this IEnumerable<BazQux> bazQuxs)
{
return bazQuxs.Select(b => new Qux(typeof(BazQux), b)).ToList();
}
public static IEnumerable<Qux> ToQuxes(this IEnumerable<BarQux> barQuxes )
{
return barQuxes.Select(b => new Qux(typeof(BarQux), b)).ToList();
}
同样,您也可以将 BarQux
或 BazQux
转换为非通用 Qux
class.
public class Qux
{
public int ID { get; set; } // Corresponds to BarQux and BazQux
public string Name { get; set; }
public Qux(Type type, object obj)
{
var ob = Convert.ChangeType(obj, type);
var quxProps = GetType().GetProperties();
var obProps = ob.GetType().GetProperties()
.Where(p =>
{
var w = quxProps.FirstOrDefault(f => f.Name == p.Name);
return w != null;
}).ToList();
foreach (var propertyInfo in obProps)
{
var val = propertyInfo.GetValue(obj);
quxProps.First(e => e.Name == propertyInfo.Name).SetValue(this, val);
}
}
}
然后您可以调用 ToFoo
扩展方法,瞧,您有一个 Foo 列表。
您还可以使用 Qux
class
的逻辑将 Foo 转换为非泛型
我真的看不出这样做有什么好处。
如果您坚持使用泛型,您可以像这样对 T 进行一些类型检查:
public class Foo<T>
{
public Foo(T obj, Qux qux)
{
var baz = obj as Baz;
var bar = obj as Bar;
Active = baz != null && baz.Active || bar != null && bar.Active;
this.FooQux = string.Format("{0} - {1}", qux.ID, qux.Name);
}
public bool Selected { get; set; }
public int ID { get; set; }
public bool Active { get; set; }
public string FooQux { get; set; }
}
或者:
public static List<Foo> ToFoo<T>(this IEnumerable<T> objCollection, IEnumerable<Qux> quxs)
{
List<Foo> foos = new List<Foo>();
foreach (T obj in objCollection)
{
var baz = obj as Baz;
var bar = obj as Bar;
Foo foo = null;
if(baz != null)
foo = new Foo(baz, quxs.Single(qux => qux.ID == baz.BazQux));
if(bar != null)
foo = new Foo(bar, quxs.Single(qux => qux.ID == bar.BarQux));
if(foo != null)
foos.Add(foo);
}
return foos;
}
但这显然并不比您已有的好多少,如果 T 不是 Bar 或 Baz(未显示),您将不得不进行一些异常处理,这会使您面临潜在的运行时故障。您也可以使用反射,但效果相同。
很遗憾,C# 无法实现您正在寻找的内容。唯一可以避免这种情况的方法是接口是鸭子类型的 a-la Golang,但事实并非如此。
根据Sandesh的建议,最终结果:
public class Foo<T>
{
public Foo() {}
public Foo(T obj, IEnumerable<Qux> quxs, string quxPropName)
{
var fooProps = GetType().GetProperties().ToList();
var tProps = typeof(T).GetProperties()
.Where(p =>
{
var w = fooProps.FirstOrDefault(f => f.Name == p.Name);
return w != null;
}).ToList();
foreach (var propertyInfo in tProps)
{
var val = propertyInfo.GetValue(obj, null);
fooProps.First(e => e.Name == propertyInfo.Name).SetValue(this, val, null);
}
int id = (int)typeof(T).GetProperty(quxPropName).GetValue(obj, null);
Qux qux = quxs.Single(q => q.ID == id);
this.FooQux = string.Format("{0} - {1}", qux.ID, qux.Name);
}
public bool Selected { get; set; }
public int ID { get; set; }
public bool Active { get; set; }
public string FooQux { get; set; }
}
以及通用扩展方法:
public static List<Foo<T>> ToFoo<T>(this IEnumerable<T> list, IEnumerable<Qux> quxs, string quxPropName)
{
List<Foo<T>> foos = null;
try
{
foos = list.Select(obj => new Foo<T>(obj, quxs, quxPropName)).ToList();
}
catch
{
foos = new List<Foo<T>>();
}
return foos;
}
和用法:
List<Foo<Bar>> bars = barCollection.ToFoo(quxCollection, "BarQux");
List<Foo<Baz>> bazs = bazCollection.ToFoo(quxCollection, "BazQux");
在一项服务中(我无法更改)我有两个对象 classes Bar
和 Baz
具有大部分相似的属性( 但遗憾的是不,它们不是从相同的基础派生而来 class 或从相同的接口继承...是的——愚蠢 ),以及依赖性 class 与其相关的 BarQux
和 BazQux
属性相关:
public class Bar public class Baz
{ {
public int ID { get; set; } public int ID { get; set; }
public bool Active { get; set; } public bool Active { get; set; }
public int BarQux { get; set; } public int BazQux { get; set; }
... ...
} }
public class Qux
{
public int ID { get; set; } // Corresponds to BarQux and BazQux
public string Name { get; set; }
}
在 WPF 屏幕中,我将每种类型的列表(Baz
和 Bar
)绑定到两个单独的 ListView。我需要每个人都有一个额外的 Selected
CheckBox 列。为此,我创建了一个具有公共属性的包装器 class、附加的 Selected
属性 以及每个属性的构造函数:
public class Foo
{
public Foo(Bar bar, Qux qux)
{
this.Active = bar.Active;
this.FooQux = string.Format("{0} - {1}", qux.ID, qux.Name);
...
}
public Foo(Baz baz, Qux qux)
{
this.Active = baz.Active;
this.FooQux = string.Format("{0} - {1}", qux.ID, qux.Name);
...
}
public bool Selected { get; set; }
public int ID { get; set; }
public bool Active { get; set; }
public string FooQux { get; set; }
...
}
为了将 class Baz
和 Bar
的每个集合转换为 Foo
的集合,我创建了以下扩展方法:
public static List<Foo> ToFoo(this IEnumerable<Bar> bars, IEnumerable<Qux> quxs)
{
List<Foo> foos = new List<Foo>();
foreach (Bar bar in bars)
{
Foo foo = new Foo(bar, quxs.Single(qux => qux.ID == bar.BarQux));
foos.Add(foo);
}
return foos;
}
public static List<Foo> ToFoo(this IEnumerable<Baz> bazs, IEnumerable<Qux> quxs)
{
List<Foo> foos = new List<Foo>();
foreach (Baz baz in bazs)
{
Foo foo = new Foo(baz, quxs.Single(qux => qux.ID == baz.BazQux));
foos.Add(foo);
}
return foos;
}
问题:
如何使它通用?
理论、实现和错误:
因为除了
Bar
和Baz
参数之外,构造函数实际上是相同的,我可以以某种方式使用泛型类型T
来制作一个构造函数并仍然抓取属性?public class Foo<T> { public Foo(T obj, Qux qux) { this.Active = obj.Active; // 'T' does not contain a definition for 'Active'... this.Qux = string.Format("{0} - {1}", qux.ID, qux.Name); ... } ... }
更改构造函数以接收整个
Qux
对象集合并在那里执行quxs.Single(qux => qux.ID == object.ObjectQux)
逻辑。然后将扩展方法变成一个泛型方法,如下所示。public static List<Foo> ToFoo<T>(this IEnumerable<T> objCollection, IEnumerable<Qux> quxs) { List<Foo> foos = new List<Foo>(); foreach (T obj in objCollection) { Foo foo = new Foo(obj, quxs); // The best overloaded method... has some invalid arguments. foos.Add(foo); } return foos; }
1和2合并?有什么我没有想到的吗?
如果您的属性有限并且列表中的项目数量也很少,那么您可以使用Reflection
。由于您将在 WPF 中使用它,我还建议将该进程移动到单独的后台线程。
通用 Foo
public class Foo<T>
{
public Foo(T obj, Qux qux)
{
//this.Active = obj.Active;
var fooProps = GetType().GetProperties().ToList();
var tProps = typeof(T).GetProperties()
.Where(p =>
{
var w = fooProps.FirstOrDefault(f => f.Name == p.Name);
return w != null;
}).ToList();
foreach (var propertyInfo in tProps)
{
var val = propertyInfo.GetValue(obj);
fooProps.First(e => e.Name == propertyInfo.Name).SetValue(this, val);
}
this.FooQux = string.Format("{0} - {1}", qux.ID, qux.Name);
}
public bool Selected { get; set; }
public int ID { get; set; }
public bool Active { get; set; }
public string FooQux { get; set; }
}
您的扩展方法
public static IEnumerable<Foo<Bar>> ToFoo(this IEnumerable<Bar> bars, IEnumerable<Qux> quxs)
{
return bars.
Select(bar => new Foo<Bar>(bar, quxs.Single(qux => qux.ID == bar.BarQux))).ToList();
}
public static IEnumerable<Foo<Baz>> ToFoo(this IEnumerable<Baz> bazs, IEnumerable<Qux> quxs)
{
return bazs.
Select(baz => new Foo<Baz>(baz, quxs.Single(qux => qux.ID == baz.BazQux))).ToList();
}
public static IEnumerable<Qux> ToQuxes(this IEnumerable<BazQux> bazQuxs)
{
return bazQuxs.Select(b => new Qux(typeof(BazQux), b)).ToList();
}
public static IEnumerable<Qux> ToQuxes(this IEnumerable<BarQux> barQuxes )
{
return barQuxes.Select(b => new Qux(typeof(BarQux), b)).ToList();
}
同样,您也可以将 BarQux
或 BazQux
转换为非通用 Qux
class.
public class Qux
{
public int ID { get; set; } // Corresponds to BarQux and BazQux
public string Name { get; set; }
public Qux(Type type, object obj)
{
var ob = Convert.ChangeType(obj, type);
var quxProps = GetType().GetProperties();
var obProps = ob.GetType().GetProperties()
.Where(p =>
{
var w = quxProps.FirstOrDefault(f => f.Name == p.Name);
return w != null;
}).ToList();
foreach (var propertyInfo in obProps)
{
var val = propertyInfo.GetValue(obj);
quxProps.First(e => e.Name == propertyInfo.Name).SetValue(this, val);
}
}
}
然后您可以调用 ToFoo
扩展方法,瞧,您有一个 Foo 列表。
您还可以使用 Qux
class
我真的看不出这样做有什么好处。
如果您坚持使用泛型,您可以像这样对 T 进行一些类型检查:
public class Foo<T>
{
public Foo(T obj, Qux qux)
{
var baz = obj as Baz;
var bar = obj as Bar;
Active = baz != null && baz.Active || bar != null && bar.Active;
this.FooQux = string.Format("{0} - {1}", qux.ID, qux.Name);
}
public bool Selected { get; set; }
public int ID { get; set; }
public bool Active { get; set; }
public string FooQux { get; set; }
}
或者:
public static List<Foo> ToFoo<T>(this IEnumerable<T> objCollection, IEnumerable<Qux> quxs)
{
List<Foo> foos = new List<Foo>();
foreach (T obj in objCollection)
{
var baz = obj as Baz;
var bar = obj as Bar;
Foo foo = null;
if(baz != null)
foo = new Foo(baz, quxs.Single(qux => qux.ID == baz.BazQux));
if(bar != null)
foo = new Foo(bar, quxs.Single(qux => qux.ID == bar.BarQux));
if(foo != null)
foos.Add(foo);
}
return foos;
}
但这显然并不比您已有的好多少,如果 T 不是 Bar 或 Baz(未显示),您将不得不进行一些异常处理,这会使您面临潜在的运行时故障。您也可以使用反射,但效果相同。
很遗憾,C# 无法实现您正在寻找的内容。唯一可以避免这种情况的方法是接口是鸭子类型的 a-la Golang,但事实并非如此。
根据Sandesh的建议,最终结果:
public class Foo<T>
{
public Foo() {}
public Foo(T obj, IEnumerable<Qux> quxs, string quxPropName)
{
var fooProps = GetType().GetProperties().ToList();
var tProps = typeof(T).GetProperties()
.Where(p =>
{
var w = fooProps.FirstOrDefault(f => f.Name == p.Name);
return w != null;
}).ToList();
foreach (var propertyInfo in tProps)
{
var val = propertyInfo.GetValue(obj, null);
fooProps.First(e => e.Name == propertyInfo.Name).SetValue(this, val, null);
}
int id = (int)typeof(T).GetProperty(quxPropName).GetValue(obj, null);
Qux qux = quxs.Single(q => q.ID == id);
this.FooQux = string.Format("{0} - {1}", qux.ID, qux.Name);
}
public bool Selected { get; set; }
public int ID { get; set; }
public bool Active { get; set; }
public string FooQux { get; set; }
}
以及通用扩展方法:
public static List<Foo<T>> ToFoo<T>(this IEnumerable<T> list, IEnumerable<Qux> quxs, string quxPropName)
{
List<Foo<T>> foos = null;
try
{
foos = list.Select(obj => new Foo<T>(obj, quxs, quxPropName)).ToList();
}
catch
{
foos = new List<Foo<T>>();
}
return foos;
}
和用法:
List<Foo<Bar>> bars = barCollection.ToFoo(quxCollection, "BarQux");
List<Foo<Baz>> bazs = bazCollection.ToFoo(quxCollection, "BazQux");