将通用数据存储在非泛型类中

时间:2009-06-18 16:09:22

标签: c# generics

我有一个DataGridView,我想用它来存储通用数据。我想在DataGridView类中保留一个类型化数据列表,以便可以在内部处理所有排序等。但是我不想在DataGridView上设置类型,因为在调用InitializeData方法之前我不会知道数据类型。

public class MyDataGridView : DataGridView {
    private List<T> m_data;
    public InitializeData<T>(List<T> data) {
       m_data = data;
    }
    ... internal events to know when the datagrid wants to sort ...
    m_data.Sort<T>(...)
}

这可能吗?如果是这样,怎么样?

3 个答案:

答案 0 :(得分:4)

如果在调用InitializeData之前不知道类型,那么类型显然不能是对象的编译时部分。

当您致电InitializeData<T>时,您是否知道有关排序的所有信息?如果是这样,你会怎样做:

private IList m_data;
private Action m_sorter;

public InitializeData<T>(List<T> data)
{
    m_data = data;
    // This captures the data variable. You'll need to
    // do something different if that's not good enough
    m_sorter = () => data.Sort();
}

然后,当您需要稍后排序时,您只需拨打m_sorter()

如果您可能对不同的事情进行排序,则可能会将其从Action更改为Action<string>或您需要排序的任何内容。

答案 1 :(得分:1)

如果乔恩的答案不够充分,那么这里的方法就更为普遍(但更具参与性,可能更令人困惑):

/// <summary>
/// Allows a list of any type to be used to get a result of type TResult
/// </summary>
/// <typeparam name="TResult">The result type after using the list</typeparam>
interface IListUser<TResult>
{
    TResult Use<T>(List<T> list);
}

/// <summary>
/// Allows a list of any type to be used (with no return value)
/// </summary>
interface IListUser
{
    void Use<T>(List<T> list);
}

/// <summary>
/// Here's a class that can sort lists of any type
/// </summary>
class GenericSorter : IListUser
{
    #region IListUser Members

    public void Use<T>(List<T> list)
    {
        // do generic sorting stuff here
    }

    #endregion
}

/// <summary>
/// Wraps a list of some unknown type.  Allows list users (either with or without return values) to use the wrapped list.
/// </summary>
interface IExistsList
{
    TResult Apply<TResult>(IListUser<TResult> user);
    void Apply(IListUser user);
}

/// <summary>
/// Wraps a list of type T, hiding the type itself.
/// </summary>
/// <typeparam name="T">The type of element contained in the list</typeparam>
class ExistsList<T> : IExistsList
{

    List<T> list;

    public ExistsList(List<T> list)
    {
        this.list = list;
    }

    #region IExistsList Members

    public TResult Apply<TResult>(IListUser<TResult> user)
    {
        return user.Use(list);
    }

    public void Apply(IListUser user)
    {
        user.Use(list);
    }

    #endregion
}

/// <summary>
/// Your logic goes here
/// </summary>
class MyDataGridView
{
    private IExistsList list;
    public void InitializeData<T>(List<T> list)
    {
        this.list = new ExistsList<T>(list);
    }

    public void Sort()
    {
        list.Apply(new GenericSorter());
    }
}

答案 2 :(得分:0)

您应该为运行时需要执行的任何通用操作定义delgates或接口。正如Jon Skeet所提到的,如果您在编译时不知道类型,则无法强类型化数据网格。

这是框架的方式。例如:

Array.Sort();

有几种方法可以使用:

这是您如何处理问题的示例。在最基本的层面上,您的场景可以通过strategy pattern来解决,这就是Array.Sort()所做的。

如果需要在运行时动态地对事物进行排序,我将创建一个IComparer类,它将要排序的列作为其构造函数中的参数进行排序。然后在比较方法中,使用该列作为排序类型。

以下是使用一些基本示例类如何执行此操作的示例。一旦你设置了这些类,你就可以将它们传递到数据网格并在适当的时候使用它们。

public class Car
{
    public string Make { get; set; }
    public string Model { get; set; }
    public string Year { get; set; }
}

public class CarComparer : IComparer
{
    string sortColumn;

    public CarComparer(string sortColumn)
    {
        this.sortColumn = sortColumn;
    }

    public int Compare(object x, object y)
    {
        Car carX = x as Car;
        Car carY = y as Car;
        if (carX == null && carY == null)
            return 0;
        if (carX != null && carY == null)
            return 1;
        if (carY != null && carX == null)
            return -1;

        switch (sortColumn)
        {
            case "Make":
                return carX.Make.CompareTo(carY.Make);
            case "Model":
                return carX.Model.CompareTo(carY.Model);
            case "Year":
            default:
                return carX.Year.CompareTo(carY.Year);
        }
    }
}