【问题标题】:c# generic with supplied parameter names具有提供的参数名称的 c# 泛型
【发布时间】:2023-01-22 22:18:50
【问题描述】:

我正在尝试创建一个通用函数,该函数可以获取具有开始日期时间和结束日期时间的对象列表,如果它们彼此紧接且没有间隙,则将它们组合起来。

public static IEnumerable<T> MakeBlocks<T>(IEnumerable<T> input)
{
    List<T> outList = new List<T>();
    if (input.Count() == 0) return outList;

    T thisEntry = input.First();
    foreach (var nextEntry in input.Skip(1))
    {
        if ( nextEntry != null && nextEntry.StartDT == thisEntry.EndDT)
        {
            thisEntry.EndDT = nextEntry.EndDT;
        }
        else
        {
            outList.Add(thisEntry);
            thisEntry = nextEntry;
        }
    }
    outList.Add(thisEntry);

    return outList;
}

如果我知道“From”和“to”属性的名称,这就可以正常工作,但是我如何使用泛型来做到这一点?

上述伪示例中的“未知”属性称为 StartDT 和 EndDT,但它们可以称为任何名称。

在 JavaScript 中,我可以只提供属性名称作为字符串,但在 C# 中不行。

这可能吗?如何实现?

【问题讨论】:

  • 为什么在编写代码时没有已知的属性名称?
  • 您还可以使用 lambda 来告诉函数要访问和设置哪些属性,以避免必须使用特定的基类。如果您编写的类不能全部使用相同的基类,这将很有帮助。

标签: c# generics


【解决方案1】:

您可以使用通用约束。因此,您将拥有一个所有 T 都继承自的类。像这样:

修改方法以使用通用约束

public static IEnumerable<T> MakeBlocks<T>(IEnumerable<T> input) where T : SomeClass
{
    List<T> outList = new List<T>();
    if (input.Count() == 0) return outList;

    T thisEntry = input.First();
    foreach (var nextEntry in input.Skip(1))
    {
        if (nextEntry != null && nextEntry.StartDT == thisEntry.EndDT)
        {
            thisEntry.EndDT = nextEntry.EndDT;
        }
        else
        {
            outList.Add(thisEntry);
            thisEntry = nextEntry;
        }
    }
    outList.Add(thisEntry);

    return outList;
}

所有 T 都应继承的基类

public abstract class SomeClass
{
    public DateTime EndDT { get; set; }
    public DateTime StartDT { get; set; }
}

【讨论】:

  • 这很有道理..
  • 我实际上建议使用对象必须实现的接口,具有特定的属性名称,而不是基类。一些提问者的类可能已经有基类,并且它们可能(无论出于何种原因)不可更改。
【解决方案2】:

如果您传递 Getter 和 Setter 函数,这是可能的:

    public static IEnumerable<T> MakeBlocks<T>(IEnumerable<T> input, 
       Func<T, DateTime> getStartProperty, 
       Func<T, DateTime> getEndProperty, 
       Action<T, DateTime> setEndProperty) {

        List<T> outList = new List<T>();
        if (!input.Any()) return outList;
    
        T thisEntry = input.First();
        foreach (var nextEntry in input.Skip(1))
        {
            if ( nextEntry != null && getStartProperty(nextEntry) == getEndProperty(thisEntry))
            {
                setEndProperty(thisEntry, getEndProperty(nextEntry));
            }
            else
            {
                outList.Add(thisEntry);
                thisEntry = nextEntry;
            }
        }
        outList.Add(thisEntry);

        return outList;
        
    }

所以你会用一些 lambda 函数调用它:

var result = MakeBlocks<MyClass>(
    myCollection, 
    mc => mc.StartDate, 
    mc => mc.EndDate, 
    (mc, value) => mc.EndDate = value
);

您还可以——付出更多努力——使用 Linq 表达式来提供两个 Getters,并使用它们来确定属性名称并合成一个 Setter。但是,更多的努力。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多