如何将通用类型转换为列表

时间:2012-01-20 17:47:42

标签: c# generics

我在我的基类中定义了一个抽象方法来接受Generic类型。我想要做的是将我的一个子类的列表传递给这个抽象方法。我只是不确定如何在子类中正确处理它?<​​/ p>

基类抽象方法:

protected abstract void Validate<T>(T records);

子类实施(我遇到问题的地方):

调用方法(传入LogEventRecord列表):

Validate<List<LogEventRecord>>(records);

方法(想要处理LogEventRecord列表):

      protected override void Validate<T>(T records)
        {
          //How do I handle records here? I want them to be List<LogEventRecord> and when i debug
          //They appear to be coming in that way, but I can't utilize them in the method
          //How do I cast the records to List<LogEventRecord>? Is that what I do?

        }

任何建议都将不胜感激。

3 个答案:

答案 0 :(得分:6)

听起来你的方法确实有错误的签名。参数名称为复数的事实,但它只是接受T意味着。我怀疑它应该是:

protected abstract void Validate<T>(IEnumerable<T> records);

然后在您的实现中,您可以简单地迭代记录。话虽如此,如果您的子类只能验证LogEventRecord的集合,那么真的应该使类型参数成为类的一部分,而不是方法的一部分:

public class Foo<T>
{
    protected abstract void Validate(IEnumerable<T> records);
}

然后您的子类将是:

public class Bar : Foo<LogRecord>
{
    protected override void Validate(IEnumerable<LogRecord> records)
    {
        foreach (LogRecord record in records)
        {
            ...
        }
    }
}

如果没有更多的背景,很难知道这是否合适。如果它立即适用,那么您可能需要更多地打破课程。

答案 1 :(得分:5)

声明这样的方法:

protected abstract void Validate<T>(IEnumerable<T> records)

并称之为:

Validate(records); 

编译器应该能够推断泛型方法的类型,不需要明确地包含它。如果你真的想要包含类型或者由于某种原因类型推断失败(你会知道这是因为编译器会抱怨你),你可以这样做:

Validate<LogEventRecord>(records);

然后您的实现可以使用这样的记录:

protected override void Validate<T>(IEnumerable<T> records)
{
    //you can use a foreach loop
    foreach (T record in records)
    {

    }

    //Or "linq" operators:
    bool isValid = records.Any(r => r.IsNotValid);

    //Or linq query comprehension syntax:
    bool isValid2 = (from r in records 
                  where r.IsNotValid
                  select r).Any();

}

但是现在你对T唯一了解的是它是一个对象。对于“object”类型的变量,你不能做太多,所以这还不是很有用。实际上,我的示例中的后两个选项现在都会失败,因为.IsNotValid属性可能不存在。

相反,您可能希望(可能已经有)一个接口来描述您将使用此函数的对象:它们可能总是要么是日志,要么是某些常见基类型的记录。如果是这种情况,您有两种选择。第一个是约束你的泛型类型。您可以通过更改方法签名来执行此操作:

protected abstract void Validate<T>(IEnumerable<T> records) where T : MyRecordInterface

另一个选择是在C#4中对接口的方差有新的支持(包括IEnumerabe&lt; T&gt;这将允许你完全避免使用泛型方法。为了利用这一点,只需确保您在Visual Studio 2010或更高版本中使用.Net 4并声明如下方法:

protected abstract void Validate(IEnumerable<MyRecordInterface> records)

您需要.Net 4,而不仅仅是针对早期版本的框架的c#4,因为您需要IEnumerable&lt; T&gt;的.Net 4版本。构建的接口包含对方差的必要支持。

最后,还有第三种选择。您可以在方法中添加delegate参数,将列表中的每个项目转换为布尔值,如下所示:

protected abstract void Validate<T>(IEnumerable<T> records, Func<T, bool> isValid)

protected override void Validate<T>(IEnumerable<T> records, Func<T, bool> isValid)
{
    bool recordsAreValid = records.Any(r => !isValid(r));
}

Validate(records, l => ((LogEventRecord)l).IsValid);

答案 2 :(得分:1)

明确使用列表通常对我有用。

protected override void Validate<T>(List<T> records)

在没有类型参数的情况下调用它,编译器会弄明白:Validate(records)