3

我有一个基类 Foo ,它具有一个属性,用于从数据库项中识别主键。其他类从我的基类 Foo 继承,并包含这些类的特性的另一个属性。每个下降的类代表数据库中的一个不同的表。

通常,当加载这些数据时,这些项目存储在相同类型的 List<Foo> 列表中。

当我需要使用某些项目时,我会根据主键找到它。

当这些项目具有相同的主键时,就会出现我的问题,并且当从下降的类 FooB 中查找项目时,最终会找到一个类 FooA,因为这是首先出现的,然后执行强制转换为 FooB 的地方会引发异常。

var list = new List<Foo>();

list.Add(new FooA() { PrimaryKey = 1 });
list.Add(nwe FooB() { PrimaryKey = 1 });

public Foo FindItem(int pk)
{
    return list.First(it => it.PrimaryKey == 1);
}

在很多地方我都使用这种方法来根据我的需要找到我的物品。

var item = (FooB)FindItem(1);

在其他类:

var item = (FooA)FindItem(1);

在我的情况下,此代码在系统中的许多地方使用,将 List<Foo> 更改为下降项目的列表是一种解决方案,但这意味着在许多地方进行更改,因为我无法在我使用的任何地方传递每个项目的类型.

我需要一个不会更改基本列表的解决方案。

4

3 回答 3

3

您将需要重写该FindItem(1)方法以返回的不是一个实例,Foo而是一个包含FooA实例 withPrimaryKey==1FooB实例 with的特殊对象PrimaryKey==1

第二步是提供转换运算符,将这个特殊对象转换为您需要的类型,以便代码var item = (FooB)FindItem(1)按预期工作。

下面是实现这种解决方案的完整工作代码。主要缺点是您必须为您使用的每个派生类型提供强制转换运算符实现。您也可以派生FooWrapperFoo(在这种情况下非常简单,只有一个属性,Foo但这取决于您的实际Foo类)。

using System;
using System.Collections.Generic;
using System.Linq;

namespace FooWrapper
{
    class Program
    {
        private static List<Foo> list = new List<Foo>();

        static FooWrapper FindItem(int id)
        {
            return new FooWrapper(list.Where(o => o.PrimaryKey == id));
        }

        static void Main(string[] args)
        {
            list.Add(new FooA() { PrimaryKey = 1, Title = "aaa" });
            list.Add(new FooB() { PrimaryKey = 1, Age = 123 });

            // test the implicit cast to Foo type. this prefers the FooA type.
            Foo item = FindItem(1);
            Console.WriteLine(item.PrimaryKey + " - " + item.GetType().Name);

            // retrieve the FooWrapper and cast it to FooA type.
            var itema = (FooA)FindItem(1);
            Console.WriteLine(itema.PrimaryKey + " - " + itema.Title + " - " + itema.GetType().Name);

            // retrieve the FooWrapper and cast it to FooB type. The wrapper instance 
            // retrieved is the same, but the cast retrieves the correct type
            var itemb = (FooB)FindItem(1);
            Console.WriteLine(itemb.PrimaryKey + " - " + itemb.Age + " - " + itemb.GetType().Name);
        }
    }

    public sealed class FooWrapper
    {
        private FooA _a;
        private FooB _b;

        public FooWrapper(IEnumerable<Foo> items)
        {
            FooA a;
            FooB b;
            foreach (var i in items)
            {
                a = i as FooA;
                if (a != null)
                {
                    this._a = a;
                }
                else
                {
                    b = i as FooB;
                    if (b != null)
                        this._b = b;
                }
            }
        }

        public static implicit operator Foo(FooWrapper obj)
        {
            if (obj == null)
                return null;

            return obj._a == null ? (Foo)obj._b : obj._a;
        }

        public static explicit operator FooA(FooWrapper obj)
        {
            if (obj == null)
                return null;

            return obj._a;
        }

        public static explicit operator FooB(FooWrapper obj)
        {
            if (obj == null)
                return null;

            return obj._b;
        }
    }

    public abstract class Foo
    {
        public int PrimaryKey { get; set; }
    }

    public class FooA : Foo
    {
        public string Title { get; set; }
    }

    public class FooB : Foo
    {
        public int Age { get; set; }
    }
}
于 2013-05-22T12:49:15.787 回答
0
public T FindItem<T>(int pk) where T : Foo
{
    return list.OfType<T>().First(it => it.PrimaryKey == 1);
}

用法:

var item = FindItem<FooB>(1);

OfType<T>对 有两个有益的影响list

  • 它将列表项过滤为类型T
  • 它返回一个IEnumerable<T>,因此 的返回值FindItem已经正确输入,不需要强制转换。
于 2013-05-22T12:31:55.917 回答
0

在 FindItem 函数中使用泛型:

public T FindItem<T> (int pk) where T : Foo
{
    return list.OfType<T>().SingleOrDefault(it => it.PrimaryKey == pk);
}
于 2013-05-22T12:35:09.327 回答