使用多个类/接口进行列表过滤
本文关键字:列表 过滤 接口 | 更新日期: 2023-09-27 18:15:38
基本上我有一个对象列表,其中每个对象可能实现一组不同的接口:
List<BaseObject> objects;
class BaseObject
{
public void DoStuff();
}
interface IX
{
void DoX();
}
interface IY
{
void DoY();
}
interface IZ
{
void DoZ();
}
我想这样写:
foreach(var obj in objects.OfType<BaseObject and IX>)
{
obj.DoStuff();
obj.DoX();
}
(如。i对BaseObject和IX类型的对象执行特定的算法,而不必在那里进行类型转换)
可以在c#中实现吗?最优雅的解决方案是什么?
我可以这样做:
foreach(var obj in objects.OfType<IX>)
{
var baseobj = (BaseObject)obj.DoStuff();
obj.DoX();
}
但我觉得它很丑。
我可能需要对实现接口IX和接口IZ的类型应用特定的操作。
foreach(var obj in objects.OfType<BaseType and IX and IZ>)
{
obj.DoStuff();
obj.DoX();
obj.DoZ();
}
一种可能的方法是使用dynamic
:
foreach(var xz in objects.Where(o => o is IX && o is IZ).Select(o => (dynamic)o))
{
xz.DoStuff();
xz.DoX();
xz.DoZ();
}
当然,如果你不想使用dynamic
,你仍然可以强制转换:
foreach(var xz in objects.Where(o => o is IX && o is IZ))
{
xz.DoStuff();
((IX)xz).DoX();
((IZ)xz).DoZ();
}
不可能完全做到你想做的。
你可以这样做。
首先,您只想对实现IX
, IY
和IZ
的类型进行操作?然后将.OfType
方法链接在一起。OfType
将为您过滤列表:
foreach (var obj in objects.OfType<IX>().OfType<IY>().OfType<IZ>()) {
不幸的是,obj
只能是强类型的IZ
,因为没有实现所有3个接口的类型。因此,您仍然需要强制转换,但可以保证强制转换可以工作:
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.
}
避免大量类型转换和类型测试的最简洁的方法是引入一个扩展方法来为您处理它。
试试这个:
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);
}
}
}
现在,我假设在最后一个循环示例中,只有当对象同时实现了IX
&IZ
。所以你可以这样写:
foreach(var obj in objects)
{
obj.DoAs<IX>(x =>
x.DoAs<IZ>(z =>
{
obj.DoStuff();
x.DoX();
z.DoZ();
}));
}
你的第一个/中间循环不需要任何新的扩展方法,因为我认为这应该足够简单:
foreach(var obj in objects.OfType<IX>())
{
(obj as BaseObject).DoStuff();
obj.DoX();
}
有了这些类和接口…
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 { }
假设用上面定义的任何类填充对象
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
}
那应该就是你要找的了。
根据您的评论更新
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
}
已更新没有类型转换。
需要另一个接口。(另外,DoStuff()应该在所有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();
}
你也可以做一些CRAZY,比如:
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();
}
Within DoStuff。
if (this is IX) { (this as IX).DoX(); }
…对其他接口也一样