制作包装类Generic的扩展方法/构造函数

时间:2015-09-24 18:11:40

标签: c# wpf generics wrapper

在服务中(我无法改变)我有两个对象类BarBaz,它们具有大致相似的属性(但遗憾的是,它们不是' t派生自相同的基类或从相同的接口继承...是 - 哑),以及与其相对BarQuxBazQux属性相关的依赖类:< / p>

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)绑定到两个单独的ListView。我需要每个都有一个额外的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; }
    ...
}

要将班级BazBar的每个集合转换为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来构造一个构造函数并仍然获取属性吗? / p>

    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组合在一起?还有什么我没有想过的?

3 个答案:

答案 0 :(得分:1)

如果您的属性有限且列表中的项目数量也很少,那么您可以使用Reflection。由于您将在WPF中使用它,我还建议将该过程移动到单独的后台线程。

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

同样,您也可以将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扩展程序,即可获得Foo列表。

您还可以使用Qux class

的逻辑将Foo转换为非泛型

答案 1 :(得分: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#无法满足您的需求。唯一可以解决这个问题的方法就是接口是鸭式的a-la Golang,但事实并非如此。

答案 2 :(得分: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");