简体   繁体   English

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

[英]Store generic data in a non-generic class

I have a DataGridView that I want to use to store generic data. 我有一个DataGridView,我想用来存储通用数据。 I want to keep a typed data list in the DataGridView class so that all of the sorts, etc. can be handled internally. 我想在DataGridView类中保留类型化的数据列表,以便可以在内部处理所有排序等。 But I don't want to have to set the type on the DataGridView since I won't know the data type until the InitializeData method is called. 但是我不想在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>(...)
}

Is this possible? 这可能吗? If so, how? 如果是这样,怎么办?

If you won't know the type until you call InitializeData , then the type clearly can't be a compile-time part of the object. 如果在调用InitializeData之前不知道类型,则该类型显然不能成为对象的编译时部分。

Do you know everything you need to know about the sorting when you call InitializeData<T> ? 您知道在调用InitializeData<T>时需要了解的有关排序的所有信息吗? If so, how about you do something like: 如果是这样,您如何做类似的事情:

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

Then when you need to sort later, you can just call m_sorter() . 然后,当您以后需要排序时,只需调用m_sorter()

If you might sort on different things, you could potentially change it from an Action to Action<string> or whatever you'd need to be able to sort on. 如果您可能对其他事物进行排序,则可以将其从Action更改为Action<string>或需要进行排序的任何内容。

If Jon's answer isn't sufficient, here's a more general (but more involved, and probably somewhat more confusing) approach: 如果乔恩的答案还不够,这是一种更通用的方法(但涉及更多,并且可能会更加令人困惑):

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

You should define delgates or an interface for any generic operations you need to perform at runtime. 您应该为运行时需要执行的任何常规操作定义代理或接口。 As Jon Skeet mentioned, you can't strongly-type your data grid if you don't know the types at compile time. 正如Jon Skeet所提到的,如果您在编译时不知道数据网格的类型,则不能对其进行强类型化。

This is the way the framework does it. 这就是框架的工作方式。 For example: 例如:

Array.Sort();

Has a few ways it can be used: 有几种使用方式:

This is an example of how you approach the problem. 这是如何解决问题的一个示例。 At its most basic level, your scenario can be solved by a strategy pattern , which is what Array.Sort() does. 从最基本的角度来看,您的情况可以通过Array.Sort()所做的策略模式来解决。

If you need to sort by things dynamically at run time, I would create an IComparer class that takes the column you want to sort by as an argument in its constructor. 如果您需要在运行时动态地对事物进行排序,我将创建一个IComparer类,该类将要排序的列作为其构造函数中的参数。 Then in your compare method, use that column as the sort type. 然后,在您的compare方法中,将该列用作排序类型。

Here is an example of how you would do it using some basic example classes. 这是一个使用一些基本示例类的示例。 Once you have these classes set up, then you'd pass both into your data grid and use them where appropriate. 一旦设置了这些类,就可以将它们都传递到数据网格中,并在适当的地方使用它们。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM