Reputation: 9204
I have defined an abstract method in my base class to take in a Generic type. What I want to be able to do is pass in a list of one of my sub classes to this abstract method. I am just not sure how to properly handle this in the sub class?
Base Class Abstract Method:
protected abstract void Validate<T>(T records);
SubClass Implementation (Where I am having issues):
Call Method (Pass in List of LogEventRecord):
Validate<List<LogEventRecord>>(records);
Method (Want to handle List of 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?
}
Any suggestions would be greatly appreciated.
Upvotes: 0
Views: 593
Reputation: 416149
Declare the method like this:
protected abstract void Validate<T>(IEnumerable<T> records)
And call it like this:
Validate(records);
The compiler should be able to infer the type for generic methods, no need to include it explicitly. If you really want to include the type or if type inference fails for some reason (you'll know this because the compiler will complain at you), you can do this:
Validate<LogEventRecord>(records);
Then your implementations can use the records like this:
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();
}
But right now the only thing you know about T
is that it's an object. You can't do much with variables of type "object", and so this isn't very useful yet. In fact, the latter two options in my sample will fail right now because the .IsNotValid
property probably doesn't exist.
Instead, you probably want (maybe already have) an interface describing the objects you will use with this function: they're probably always going to be either logs or records of some common base type. If that's the case, you have a two options. The first is to constrain your generic type. You do that by changing your method signature like this:
protected abstract void Validate<T>(IEnumerable<T> records) where T : MyRecordInterface
The other option is that in C# 4 there is new support for variance on interfaces (including IEnumerabe<T> that would allow you to avoid the need for a generic method here at all. To take advantage of this, just make sure you're using .Net 4 in Visual Studio 2010 or later and declare the method like this:
protected abstract void Validate(IEnumerable<MyRecordInterface> records)
You need .Net 4, and not just c# 4 targeting an earlier version of the framework, because you need the .Net 4 version of the IEnumerable<T> interface which is built to include the necessary support for variance.
Finally, there is a third option. You could add a delegate
parameter to your method to convert each item in the list to a boolean, like so:
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);
Upvotes: 5
Reputation: 2476
Using a list explicitly usually works better for me.
protected override void Validate<T>(List<T> records)
Call it without a type parameter, the compiler will figure it out: Validate(records)
Upvotes: 1
Reputation: 1503839
It sounds like really your method has the wrong signature. The fact that the parameter name is plural but it's just accepting a T
implies that. I suspect it should really be:
protected abstract void Validate<T>(IEnumerable<T> records);
Then in your implementations, you can just iterate over the records simply. Having said that, if your subclass can only validate a collection of LogEventRecord
it could well be that you really should make the type parameter part of the class, not part of the method:
public class Foo<T>
{
protected abstract void Validate(IEnumerable<T> records);
}
then your subclass would be:
public class Bar : Foo<LogRecord>
{
protected override void Validate(IEnumerable<LogRecord> records)
{
foreach (LogRecord record in records)
{
...
}
}
}
It's hard to know whether this is appropriate without more context though. If it's not immediately appropriate, it's possible that you'll need to break your classes out more.
Upvotes: 6