tags:

views:

124

answers:

3

Hi,

I have a base class called LabFileBase. I have constructed a List and have added my derived classes to it. I want to search the List for a particular object based on a key I've defined. The problem I'm having is how do you downcast in a LINQ expression?

Here is some sample code:

public abstract class LabFileBase
{
}

public class Sample1 : LabFileBase
{    
    public string ID {get;set;}
    public string Name {get;set;}
    //..
}

public class Sample2 : LabFileBase
{    
    public string ID {get;set;}
    public string Name {get;set;}
    //..
}

I want to search for a particular Sample2 type, but I need to downcast if i used a regular foreach loop like this:

foreach(var s in processedFiles)  //processedFiles is a List<LabFileBase>
     if (s is Sample2)
         var found = s as Sample2;
             if (found.ID = ID && found.Name == "Thing I'm looking for")
                  //do extra work

I would much rather have something like this:

var result = processedFiles.Select(s => s.ID == SomeID && s.Name == SomeName);

Is this possible and what syntactic punctuation is involved, or is the foreach my only option because of the different objects. Sample1 and Sample2 only have ID and Name as the same fields.

EDIT: Thanks to all for your support and suggestions, I've entered almost everything into the backlog to implement.

+4  A: 

Why not put ID and Name into the base class? Then you don't need any downcasting.

If you do need casting though, the Cast and OfType operators may be helpful to you. Each transforms a sequence into a sequence of the specified type: Cast assumes that each element is the right type (and throws an exception if that's not the case); OfType works more like the C# as operator, only returning elements which happen to be the right type and ignoring others.

Jon Skeet
seq.OfType<MyFavType>() works like seq.Where(el => el is MyFavType).
Justice
@Jon: I will definitely consider moving ID and Name to the base class. The more I think about it, they really belong there.
Chris
@Justice: Not quite, because it *also* ends up with a sequence of the target type. It's like seq.Where(...).Cast<...>()
Jon Skeet
A: 

Interesting question. In addition to Jon Skeet's (which is better than this option) note you could try a more manual alternative. Add an additional property containing an identifier for the type of object it is.

public abstract class LabFileBase
{
    public string LabFileObjectType { get; }
    public string ID {get;set;}
    public string Name {get;set;}
}


public class Sample1 : LabFileBase
{    
    public string LabFileObjectType { get { return "Sample1"; } }
    //..
}

public class Sample2 : LabFileBase
{    
    public string LabFileObjectType { get { return "Sample2"; } }
    //..
}

Then you could query against that property as well:

var result = processedFiles.Select(s => s.ID == SomeID && s.Name == SomeName && s.LabFileObjectType == "Sample2");
Ian Suttle
Jon Skeet
You probably would. I'm saying there's an alternative from your example. That's definitely more proper.
Ian Suttle
+4  A: 

To elaborate on Jon's answer, OfType is almost certainly the correct operator if you aren't willing to put these fields into your base class. For example:

foreach(var s in processedFiles.OfType<Sample2>())
    if(s.ID == ID && s.Name == "Thing I'm looking for")
        // whatever

foreach(var s in processedFiles.OfType<Sample1>())
    if(s.ID == ID && s.Name == "Some other thing")
        // and so on
mquander