Basically I have a list of objects where each object might implement a different set of interfaces:
List<BaseObject> objects;
class BaseObject
{
public void DoStuff();
}
interface IX
{
void DoX();
}
interface IY
{
void DoY();
}
interface IZ
{
void DoZ();
}
And i would like to write something like this:
foreach(var obj in objects.OfType<BaseObject and IX>)
{
obj.DoStuff();
obj.DoX();
}
(E.g. i perform a specific algorithm for objects of type BaseObject and IX without having to do typecasts there)
Is it possible to do in C#? What's the most elegant solution?
I can do this:
foreach(var obj in objects.OfType<IX>)
{
var baseobj = (BaseObject)obj.DoStuff();
obj.DoX();
}
But i find it ugly.
And I might need to apply specific operations to types that implement say interface IX and interface IZ.
foreach(var obj in objects.OfType<BaseType and IX and IZ>)
{
obj.DoStuff();
obj.DoX();
obj.DoZ();
}
One possible way is to use dynamic
:
foreach(var xz in objects.Where(o => o is IX && o is IZ).Select(o => (dynamic)o))
{
xz.DoStuff();
xz.DoX();
xz.DoZ();
}
Of course, you can still cast if you don't want to use dynamic
:
foreach(var xz in objects.Where(o => o is IX && o is IZ))
{
xz.DoStuff();
((IX)xz).DoX();
((IZ)xz).DoZ();
}
It's not possible to do EXACTLY what you want.
Here's what you CAN do.
First, you want to only operate on types that implement IX
, IY
, AND IZ
? Then just chain together your .OfType
methods. OfType
will filter the list for you:
foreach (var obj in objects.OfType<IX>().OfType<IY>().OfType<IZ>()) {
Unfortunately, obj
will only be strongly-typed IZ
, because there's no such thing as a type that implements all 3 interfaces. So you still have to cast, but you're guaranteed that the cast will work:
foreach (var obj in objects.OfType<IX>().OfType<IY>().OfType<IZ>()) {
((IX)obj).DoX();
((IY)obj).DoY();
((IZ)obj).DoZ(); // Note, You could also just do obj.DoZ(), but consistency looks better.
}
The most concise way to avoid a lot of casting and type testing is to introduce an extension method that handles it for you.
Try this:
public static void DoAs<T>(this object @this, Action<T> action)
where T : class
{
var t = @this as T;
if (t != null)
{
var a = action;
if (a != null)
{
a(t);
}
}
}
Now, I assume in your last example loop that you only want to execute the loop if the object implements both IX
& IZ
. So you would then write:
foreach(var obj in objects)
{
obj.DoAs<IX>(x =>
x.DoAs<IZ>(z =>
{
obj.DoStuff();
x.DoX();
z.DoZ();
}));
}
Your first/middle loop doesn't need any new extension method as I think this should be simple enough:
foreach(var obj in objects.OfType<IX>())
{
(obj as BaseObject).DoStuff();
obj.DoX();
}
I hope this helps.
With these classes and interfaces...
List<BaseObject> objects;
class BaseObject
{
public void DoStuff();
}
interface IX
{
public void DoX();
}
interface IY
{
public void DoY();
}
interface IZ
{
public void DoZ();
}
class X : BaseObject, IX { }
class Y : BaseOjbect, IY { }
class Z : BaseObject, IZ { }
class XY : BaseObject, IX, IY { }
Asumming you populate ojbect with any Class defined above
foreach (var o in objects)
{
o.DoStuff();
if (o is IX)
((IX)o).DoX(); //executed on class X and XY
if (o is IY)
((IY)o).DoY(); //excuted on class Y and XY
if (o is IZ)
((IZ)o).DoZ(); //excuted on class Z
}
That should do what you are looking for.
UPDATED per your comment.
foreach (var o in objects)
{
o.DoStuff();
if (o is IX and o is IY)
{
// do something different only for XY
}
else if (o is IX)
((IX)o).DoX(); //executed on class X
else if (o is IY)
((IY)o).DoY(); //excuted on class Y
else if (o is IZ)
((IZ)o).DoZ(); //excuted on class Z
}
UPDATED No type casting.
Requires another interface. (Additionally, DoStuff() should be on all IX, IY, IZ).
interface IXY : IX, IY { }
// This IXY could be var, but just strongly typing it for example
foreach (IXY o in objects.OfType(IXY)
.Cast<IXY>())
{
o.DoStuff();
o.DoX();
o.DoY();
}
You could also do someting CRAZY like:
foreach (var obj in objects.OfType<IX>()
.OfType<IY>()
.OfType<IZ>()
.Select(o => new
{
AsIX = (IX)o,
AsIY = (IY)o,
AsIZ = (IZ)o
})
{
obj.AsIX.DoX();
obj.AsIY.DoY();
obj.AsIZ.DoZ();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With