具有文本 属性 的所有对象的通用比较器
Generic Comparer for all objects with a Text property
我正在尝试为所有具有文本 属性 的对象实现通用比较器(用于排序)...因此可以比较两个 ASP.net 文本框、两个标签或在此具体情况是 telerik RadTreeView 中的两个 RadTreeNode(只要它们有文本 属性)。所以我将以下内容放在一起进行尝试,但出现如下错误:
我有以下代码:
public class TextComparer<T> : IComparer
where T : IHasTextProperty
{
public int Compare(object a, object b)
{
T nodeA = (T)a;
T nodeB = (T)b;
return nodeA.Text.CompareTo(nodeB.Text);
}
}
public interface IHasTextProperty
{
string Text { get; set; }
}
然后计划像这样使用它...
Array.Sort(nodes, new TextComparer<RadTreeNode>());
但收到以下消息:
Error 6613 The type 'Telerik.Web.UI.RadTreeNode' cannot be used as
type parameter 'T' in the generic type or method 'TextComparer'.
There is no implicit reference conversion from
'Telerik.Web.UI.RadTreeNode' to 'IHasTextProperty'
我确定这是一个简单的修复,但我对如何修复它有点困惑。
您正试图在不支持 duck-typing 的 C# 中执行 duck-typing。在某些语言中,您可以根据具有特定 属性 的类型来匹配类型,例如本例中的 Text
。这仅在语言支持此技术时有效。
对于 C#,class 必须显式实现一个接口才能被视为具有该接口的类型。 Telerik.Web.UI.RadTreeNode
未实施 IHasTextProperty
。 T
仅限于实现 IHasTextProperty
的类型,因此您会看到错误。
在这种情况下你真的不能使用泛型。您需要测试 a
和 b
是否有 Text
属性。这可以使用反射或使用 dynamic
来完成。不幸的是,这两种解决方案都不会像您尝试做的那样简洁。
System.Web.UI
提供了它自己的 IHasTextProperty
,即 ITextControl
(msdn),其行为与您的 IHasTextProperty
完全相同。缺点是您无法确定 RadTreeNode
(或任何其他第 3 方控件)实现此接口。
唯一可以确定的方法是从编译时删除此检查并通过反射将其放入运行时,这很简单但可能不是您想要的。如果您仍想使用它,这里有一个在 TextComparer
的构造函数中使用 ArgumentException
的示例,以确保只比较有效的对象。
public class TextComparer<T> : IComparer
{
private bool HasTextProperty(Type t)
{
return (t.GetProperty("Text", typeof(string)) != null);
}
private string GetTextPropertyValue(object obj)
{
return obj.GetType().GetProperty("Text", typeof(string)).GetValue(obj) as string;
}
public TextComparer()
{
if (!HasTextProperty(typeof(T))) throw new ArgumentException(string.Format("{0} doesn't provide a Text property", typeof(T).Name), "T");
}
public int Compare(object x, object y)
{
return GetTextPropertyValue(x).CompareTo(GetTextPropertyValue(y));
}
}
我正在尝试为所有具有文本 属性 的对象实现通用比较器(用于排序)...因此可以比较两个 ASP.net 文本框、两个标签或在此具体情况是 telerik RadTreeView 中的两个 RadTreeNode(只要它们有文本 属性)。所以我将以下内容放在一起进行尝试,但出现如下错误:
我有以下代码:
public class TextComparer<T> : IComparer
where T : IHasTextProperty
{
public int Compare(object a, object b)
{
T nodeA = (T)a;
T nodeB = (T)b;
return nodeA.Text.CompareTo(nodeB.Text);
}
}
public interface IHasTextProperty
{
string Text { get; set; }
}
然后计划像这样使用它...
Array.Sort(nodes, new TextComparer<RadTreeNode>());
但收到以下消息:
Error 6613 The type 'Telerik.Web.UI.RadTreeNode' cannot be used as type parameter 'T' in the generic type or method 'TextComparer'. There is no implicit reference conversion from 'Telerik.Web.UI.RadTreeNode' to 'IHasTextProperty'
我确定这是一个简单的修复,但我对如何修复它有点困惑。
您正试图在不支持 duck-typing 的 C# 中执行 duck-typing。在某些语言中,您可以根据具有特定 属性 的类型来匹配类型,例如本例中的 Text
。这仅在语言支持此技术时有效。
对于 C#,class 必须显式实现一个接口才能被视为具有该接口的类型。 Telerik.Web.UI.RadTreeNode
未实施 IHasTextProperty
。 T
仅限于实现 IHasTextProperty
的类型,因此您会看到错误。
在这种情况下你真的不能使用泛型。您需要测试 a
和 b
是否有 Text
属性。这可以使用反射或使用 dynamic
来完成。不幸的是,这两种解决方案都不会像您尝试做的那样简洁。
System.Web.UI
提供了它自己的 IHasTextProperty
,即 ITextControl
(msdn),其行为与您的 IHasTextProperty
完全相同。缺点是您无法确定 RadTreeNode
(或任何其他第 3 方控件)实现此接口。
唯一可以确定的方法是从编译时删除此检查并通过反射将其放入运行时,这很简单但可能不是您想要的。如果您仍想使用它,这里有一个在 TextComparer
的构造函数中使用 ArgumentException
的示例,以确保只比较有效的对象。
public class TextComparer<T> : IComparer
{
private bool HasTextProperty(Type t)
{
return (t.GetProperty("Text", typeof(string)) != null);
}
private string GetTextPropertyValue(object obj)
{
return obj.GetType().GetProperty("Text", typeof(string)).GetValue(obj) as string;
}
public TextComparer()
{
if (!HasTextProperty(typeof(T))) throw new ArgumentException(string.Format("{0} doesn't provide a Text property", typeof(T).Name), "T");
}
public int Compare(object x, object y)
{
return GetTextPropertyValue(x).CompareTo(GetTextPropertyValue(y));
}
}