在ASP.NET中查找使用特定接口的控件
尽管我觉得我错过了一些明显的事情,但在这段时间过得很愉快。我有一个继承自System.Web.UI.WebControls.Button的控件,然后实现了我设置的接口。所以想...
public class Button : System.Web.UI.WebControls.Button, IMyButtonInterface { ... }
在页面的代码背后,我想从ASPX中找到此按钮的所有实例。因为我真的不知道类型是什么,所以只知道它实现的接口,这就是我遍历控制树时要做的所有事情。事实是,我不必确定对象是否使用接口,而不必测试其类型。我该如何遍历控制树并以干净的方式提取实现" IMyButtonInterface"的任何内容(Linq会很好)?
再说一遍,知道这很明显,但是现在刚开始大量使用接口,我似乎无法集中我的Google结果来弄清楚它:)
编辑:GetType()
返回实际的类,但不返回接口,因此我无法对此进行测试(例如,它将返回"MyNamespace.Button
"而不是" IMyButtonInterface`") 。试图在递归函数中使用" as"或者" is"时,函数中甚至不会识别出type参数!这很奇怪。所以
if(ctrl.GetType() == typeToFind) //ok if(ctrl is typeToFind) //typeToFind isn't recognized! eh?
绝对在这上面挠头。
解决方案
回答
接口足够接近应该具有相同感觉的类型。我会用as运算符。
foreach (Control c in this.Page.Controls) { IMyButtonInterface myButton = c as IMyButtonInterface; if (myButton != null) { // do something } }
我们还可以根据需要使用is运算符进行测试。
if (c is IMyButtonInterface) { ... }
回答
"是"操作员会工作吗?
if (myControl is ISomeInterface) { // do something }
回答
我们可以只在界面上搜索。如果控件具有子控件(即按钮位于面板中),则这也将使用递归。
private List<Control> FindControlsByType(ControlCollection controls, Type typeToFind) { List<Control> foundList = new List<Control>(); foreach (Control ctrl in this.Page.Controls) { if (ctrl.GetType() == typeToFind) { // Do whatever with interface foundList.Add(ctrl); } // Check if the Control has Child Controls and use Recursion // to keep checking them if (ctrl.HasControls()) { // Call Function to List<Control> childList = FindControlsByType(ctrl.Controls, typeToFind); foundList.AddRange(childList); } } return foundList; } // Pass it this way FindControlsByType(Page.Controls, typeof(IYourInterface));
回答
如果我们要对该类型进行一些工作,那么TryCast是我要使用的。
Dim c as IInterface = TryCast(obj, IInterface) If c IsNot Nothing 'do work End if
回答
我们总是可以只使用as强制转换:
c as IMyButtonInterface; if (c != null) { // c is an IMyButtonInterface }
回答
Longhorn213几乎是正确的答案,但是正如肖恩·钱伯斯(Sean Chambers)和bdukes所说,我们应该使用
ctrl is IInterfaceToFind
代替
ctrl.GetType() == aTypeVariable
原因是,如果使用.GetType()
,我们将获得对象的真实类型,不一定是在继承/接口实现链中也可以将其强制转换为对象的类型。另外,.GetType()
将永远不会返回抽象类型/接口,因为我们无法创建抽象类型或者接口。 GetType()仅返回具体类型。
这不起作用的原因
if(ctrl is typeToFind)
是因为变量" typeToFind"的类型实际上是" System.RuntimeType",而不是我们将其值设置为的类型。例如,如果将字符串的值设置为"foo
",则其类型仍为字符串,而不是"foo
"。我希望这是有道理的。使用类型时很容易感到困惑。与他们一起工作时,我总是很困惑。
关于longhorn213的答案,最重要的一点是我们必须使用递归,否则我们可能会错过页面上的某些控件。
尽管这里有一个可行的解决方案,但我也很想看看LINQ是否有更简洁的方法。
回答
我将对Longhorn213的示例进行以下更改以对此进行一些整理:
private List<T> FindControlsByType<T>(ControlCollection controls ) { List<T> foundList = new List<T>(); foreach (Control ctrl in this.Page.Controls) { if (ctrl as T != null ) { // Do whatever with interface foundList.Add(ctrl as T); } // Check if the Control has Child Controls and use Recursion // to keep checking them if (ctrl.HasControls()) { // Call Function to List<T> childList = FindControlsByType<T>( ctrl.Controls ); foundList.AddRange( childList ); } } return foundList; } // Pass it this way FindControlsByType<IYourInterface>( Page.Controls );
这样,我们就可以获得不需要其他强制类型转换的所需类型的对象列表。我还对其他人指出的" as"运算符进行了必要的更改。