7
\$\begingroup\$

I'm using an interface IloadPattern on deriving 3 classes from it LinearLoadingPattern , JointsLoadingPattern , AreaLoadingPattern. At some point, I'm creating a list all the objects created from those classes.

At a later point, I want to retrieve list of a particular object (JointsLoadingPattern) from the above list, I'm wondering if there is a better way to do this.

Below is the way I'm doing it.

public List<JointsLoadingPattern> GetJointLoads()
{
    List<JointsLoadingPattern> jointLoads =  new List<JointsLoadingPattern>();

    foreach(ILoadPattern loadCase in AllLoadCases)
    {
        if(loadCase.GetType() ==typeof(JointsLoadingPattern))
        {
            jointLoads.Add((JointsLoadingPattern)loadCase);
        }
    }

    return jointLoads;
}
\$\endgroup\$
2
  • \$\begingroup\$ you could use linq: e.g. something like this: AllLoadCases.Where(case => case.GetType() == typeof(JointsLoadingPattern)).ToList() - i like the LINQ way because it is less verbose. but is exactly the same as what you are already doing right now. \$\endgroup\$
    – BenKoshy
    Commented Apr 24, 2018 at 10:49
  • 2
    \$\begingroup\$ @BKSpurgeon: Linq also provides an OfType method: AllLoadCases.OfType<JointsLoadingPattern>().ToList(). \$\endgroup\$ Commented Apr 24, 2018 at 11:21

1 Answer 1

9
\$\begingroup\$

You can use the LINQ extension method IEnumerable.OfType<TResult>(), it returns any elements matching the type of the specified generic type argument:

var jointLoads = AllLoadCases.OfType<JointsLoadingPattern>();

This will return an IEnumerable<JointsLoadingPattern>, you can always call .ToList() to convert it.

Update

As noted in the comments, since OfType<TResult> uses the is operator internally, it will also return any sub-types of TResult type. If you want to avoid this you can write your own extension method:

public static class Extensions
{
    public static IEnumerable<TResult> OfExactType<T, TResult>(this IEnumerable<T> source)
        where TResult : T
    {
        return OfExactType<TResult>(source);
    }

    public static IEnumerable<TResult> OfExactType<TResult>(this IEnumerable source)
    {
        if (source == null) throw new ArgumentNullException(nameof(source));
        return OfExactTypeImpl<TResult>(source);
    }

    private static IEnumerable<TResult> OfExactTypeImpl<TResult>(IEnumerable source)
    {
        var type = typeof(TResult);
        foreach (var t in source)
        {
            if (t.GetType() == type)
            {
                yield return (TResult)t;
            }
        }
    }
}

Example usage:

AllLoadCases.OfExactType<ILoadPattern, JointsLoadingPattern>();
AllLoadCases.OfExactType<JointsLoadingPattern>();

The main difference between the 2 being, that in 1 case you will get a compile-time error when you attempt to provide invalid types, while in the other case this, will be ignored and it will simply return nothing.

\$\endgroup\$
15
  • 2
    \$\begingroup\$ This is what I would do, but I would clarify that this will return all subtypes of JointsLoadingPattern as well, which I believe is different from what the OP was doing in their original post. \$\endgroup\$ Commented Apr 24, 2018 at 12:12
  • 1
    \$\begingroup\$ @AaronM.Eshbach since OP is using classes based on the ILoadPattern so it doesn't really matter... I doubt he has any descendants of the JointsLoadingPattern - actually it never matters and if it does then there is something else wrong with the code. I have never had any case where simple OfType wouldn't do the job. I would worry if it didn't work and I would start to think about the design again. \$\endgroup\$
    – t3chb0t
    Commented Apr 24, 2018 at 12:39
  • 1
    \$\begingroup\$ @t3chb0t, I know it shouldn't matter if everything is crafted correctly, and I would personally use OfType, I just wanted to point out that the solution is technically different from the original code. \$\endgroup\$ Commented Apr 24, 2018 at 12:44
  • 2
    \$\begingroup\$ Parameter validation and yield return doesn't mix well. To get immediate parameter validation you need to split the iteration part into a private (or local) implementation function: See Fiddle \$\endgroup\$
    – Johnbot
    Commented Apr 24, 2018 at 13:44
  • 2
    \$\begingroup\$ @t3chb0t It's eager parameter validation and fully lazy sequence evaluation. It's the best of both worlds and it's exactly how it's done in System.Linq.Enumerable in your link. Validate parameters defer enumeration. \$\endgroup\$
    – Johnbot
    Commented Apr 24, 2018 at 15:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.