3

我想为通用类创建一个包装函数,如下所示:

public class ColumnData
{
    public static ColumnData<T> Create<T>(string name, int width, ColumnType type,
                                          Func<T, string> dataFormater)
    {
        return new ColumnData<T>(name, width, type, dataFormater);
    }
}

Create方法将作为具有签名的另一个函数的参数调用:

public void populateFromData<TDATA>(IEnumerable<TDATA> data, 
                                    params ColumnData<TDATA>[] columns)   
{
   ...
}

这里的目的是能够做到:

var myData = new List<MyDataType>();
dataListView.populateFromData(
    myData,
    ColumnData.Create("ID", 40, ColumnType.Numeric, x => x.ID.ToString());

但是,Create无法根据预期的签名为其自身推断出正确的类型,因此 lambda 也不知道自己。

这是类型推断的限制,还是有办法使这个设置工作?

注意:如有必要,我愿意在此函数调用中的某处指定实际数据类型,但我不想为每个.Create().

4

3 回答 3

1

有时,当 c# 无法推断它的实际类型时,您只需要显式指定泛型类型参数。

dataListView.populateFromData(
    myData,
    ColumnData.Create<MyDataType>("ID", 40, ColumnType.Numeric, x => x.ID.ToString());
于 2012-11-29T15:30:21.007 回答
1

正如其他人所解释的那样,使用您想要的确切语法是不可能的。作为一种解决方法,您可以将打字移至单独的建筑类:

    public class ColumnDataBuilder
    {
        public static ColumnDataBuilder<T> ColumnsFor<T>(IEnumerable<T> data)
        {
            return new ColumnDataBuilder<T>(data);
        }
    }

    public class ColumnDataBuilder<T> : ColumnDataBuilder
    {
        public IEnumerable<T> Data { get; private set; }

        public ColumnDataBuilder(IEnumerable<T> data)
        {
            this.Data = data;
        }
        public ColumnData<T> Create(string name, int width, ColumnType type, Func<T, string> dataFormater)
        {
            return new ColumnData<T>(name, width, type, dataFormater);
        }

        public void populateFromData(params ColumnData<T>[] columns)
        {
            ///...
        }
    }

    public class ColumnData<T>
    {
        public ColumnData(string name, int width, ColumnType type, Func<T, string> dataFormatter)
        {

        }
    }

然后用法可能如下所示:

        var builder = ColumnDataBuilder.ColumnsFor(new List<MyDataType>());
        builder.populateFromData(builder.Create("ID", 40, ColumnType.Numeric, x => x.ID.ToString()));
        IEnumerable<MyDataType> data = builder.Data;

或者更接近您的示例用法(如果您想继续populateFromData使用dataListView),在这种情况下您可以放弃该ColumnDataBuilder<T>.populateFromData方法(因为从您的评论看来,不可能保留在那里):

        var myData = new List<MyDataType>();
        var builder = ColumnDataBuilder.ColumnsFor(myData);
        dataListView.populateFromData(myData, builder.Create("ID", 40, ColumnType.Numeric, x => x.ID.ToString()));

或者两全其美:

        var builder = ColumnDataBuilder.ColumnsFor(new List<MyDataType>());
        dataListView.populateFromData(builder.Data, builder.Create("ID", 40, ColumnType.Numeric, x => x.ID.ToString()));

编辑:考虑到您的评论,您可能不希望populateFromData甚至可能不希望IEnumerable<T> Data存储在 上ColumnDataBuilder,因此您可以简化为:

    public class ColumnDataBuilder<T> : ColumnDataBuilder
    {
        public ColumnData<T> Create(string name, int width, ColumnType type, Func<T, string> dataFormater)
        {
            return new ColumnData<T>(name, width, type, dataFormater);
        }
    }

    public class ColumnDataBuilder
    {
        public static ColumnDataBuilder<T> ColumnsFor<T>(IEnumerable<T> data)
        {
            return new ColumnDataBuilder<T>();
        }
    }

从上面的用法:

        var myData = new List<MyDataType>();
        var builder = ColumnDataBuilder.ColumnsFor(myData);
        dataListView.populateFromData(myData, builder.Create("ID", 40, ColumnType.Numeric, x => x.ID.ToString()));
于 2012-11-29T15:45:26.733 回答
0

我刚刚想出的一个答案涉及一个别名。我去掉了包装类,直接把Create方法移到ColumnData<T>类中,然后添加:

using ColumnData = ColumnData<MyDataType>;

这使我可以ColumnData.Create()使用类型提示访问编译器,而无需在每一行上指定它。我需要在要使用它的每个文件中创建别名,但这是一个可行的解决方案。

于 2012-11-29T15:54:15.297 回答