2015-09-24 105 views
3

中的服务(,我不能改变)我有两个对象类BarBaz与大多相似特性(但遗憾的是没有,他们不会从同一派生的构造函数基类或来自同一界面...是啊继承 - 哑),以及与它们相对BarQuxBazQux性能的相关性类:使扩展方法/包装类通用

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屏幕,我绑定列表每种类型(BazBar )到两个独立的ListViews。我需要每个人都有一个额外的Selected CheckBox列。要做到这一点,我创建与公共属性的包装类,附加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; } 
    ... 
} 

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; 
} 

问:

如何让我这个普通的?

理论,实施和错误:

  1. 由于构造函数是除了BarBaz参数几乎是相同的,我可以以某种方式使用泛型类型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); 
         ... 
        } 
        ... 
    } 
    
  2. 更改构造函数接收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; 
    } 
    
  3. 1和2合起来了吗?任何我没有想到的事情?

+0

你可能想看看这个https://en.wikipedia.org/wiki/Adapter_pattern – BlackBear

+0

您无法修改该文件或无法修改该文件屁股?例如,你可以实现一个部分类。但是,你将不得不将原来的类改为部分类。 –

+0

@ScottNimrod我无法修改服务中的任何类或文件,例如'Bar','Baz'和'Type'类。如果我愿意,我会很乐意让前两个从基类或接口继承 - 这会让我在有限的经验中简化事情变得更容易。 – OhBeWise

回答

1

如果您的房产数量有限且列表中的物品数量也很少,那么您可以使用Reflection。既然你会在WPF中使用它,我也会建议把这个过程移到一个单独的后台线程中。

通用富

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(); 
    } 

同样,您也可以将您的BarQuxBazQux到非通用类Qux

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扩展方法,瞧你有富的列表。

你也可以转换到富非通用使用Qux

+0

写得很好的建议。我目前正在按照这些方法测试一个想法,尽管我更喜欢你通过属性循环的方法。我不太了解反射,我很好奇这是否太昂贵,因此不值得花费时间 - 这些类共有6个属性,加上Qux属性,通常对于任何给定的对象都转换为“Foo”客户将从少数几个到几百个不等。思考?我想花一些时间来测试你的建议,然后我会回复你。 – OhBeWise

+1

查看6个属性的数量和几百条记录的情况,上述解决方案是可行的。您拥有的另一个好处是,它是要使用反射进行处理的客户端计算机。如果它已经在服务端,您可能会遇到问题,因为托管该服务的计算机将受到冲击 – Sandesh

+0

感谢您的输入!我无法对Qux类进行任何修改,因为这也是服务的一部分 - 但我不需要它们。通用类“Foo”非常好。我发布了一个答案,显示了我的确切用法,使扩展方法也是通用的。 – OhBeWise

0

的逻辑我实在看不出做这种方式的任何好处。

如果你是坚定的关于使用泛型,你可以做一些类型在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#不可能的。你可以通过这种方式逃脱的唯一方法是,如果接口是鸭式的Golang,但事实并非如此。

+1

实现包装器有一个有效的点。当我被告知可能会有更多具有相同属性名称和类型的类时,我还有一个应用程序开发中途遇到的情况,这些类也需要注意。编写更多构造函数是不可能的。所以我通过反思去照顾其他类来节省开发时间 – Sandesh

+0

我对通用实现的兴趣在于如果服务类别发生变化,那么重复代码的减少和未来的工作就会减少。很有可能是我所拥有的是足够的,泛型会更少 - 我愿意接受 - 这是一种学习体验。 – OhBeWise

0

按照由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"); 
+1

添加一些异常处理,因为这种方法也给误操作的范围,因为'ToFoo'扩展方法可以用在其他实现'IEnumerable '的类上,你可能想要防止 – Sandesh