0

我有一个 DataGridView,我需要使用自定义排序器(派生自 System.Collections.IComparer)。那工作得很好,但我注意到我的比较并没有完全正确,因为它最终会对单元格进行字符串比较,而不管它们的底层数据类型如何。(所以, 1, 10, 2) 而不是 (1, 2, 10)。

如何编写一个比较函数来适当地比较列而不考虑它们的数据类型?

public int compare(object x, object y)
{
    DataGridViewRow dr1 = (DataGridViewRow)x;
    DataGridViewRow dr2 = (DataGridViewRow)y;

    object cell1 = dr1.Cells["SomeName"].Value;
    object cell2 = dr2.Cells["SomeName"].Value;

    //Compare cell1 and cell 2 based on the data type in
    //dr1.Cells["SomeName"].ValueType.
}
4

2 回答 2

2

对于这个问题的任何解决方案,似乎都有几个基本部分。

  1. 确保您正在比较相似的类型。
  2. 确保您可以比较该类型的实例。

这里有一些想法可以帮助您入门。为了清楚起见,我省略了一些错误检查。

认为:

Type type1 = dr1.Cells["SomeName"].ValueType;
Type type2 = dr2.Cells["SomeName"].ValueType;

然后看看你是否可以将一个值强制转换为另一个类型:

if (type1 != type2)
{
    TypeConverter tc1 = TypeDescriptor.GetConverter(type1);
    TypeConverter tc2 = TypeDescriptor.GetConverter(type2);

    if (tc1.CanConvertFrom(type2))
    {
        cell2 = tc1.ConvertFrom(cell2);
        type2 = type1;
    }
    else if (tc1.CanConvertTo(type2))
    { 
        cell1 = tc1.ConvertTo(cell1, type2);
        type1 = type2;
    }
    else if (tc2.CanConvertFrom(type1))
    {
        cell1 = tc2.ConvertFrom(cell1);
        type1 = type2;
    }
    else if (tc2.CanConvertTo(type1))
    { 
        cell2 = tc2.ConvertTo(cell2, type1);
        type2 = type1;
    }
    else // fallback to string comparison
    {
        cell1 = tc1.ConvertToString(cell1);        
        type1 = cell1.GetType();

        cell2 = tc2.ConvertToString(cell2);        
        type2 = cell2.GetType();
    }
    // cell1 and cell2 should be the same type now
}

现在您有了类似类型的实例,您需要找到一种方法来比较它们。

如果您使用的是 C# 4,那么 dynamic 关键字可能是您的朋友:

dynamic c1 = cell1;
try 
{
    int compareResult = c1.CompareTo(cell2);
}
catch(Exception)
{
    // type1 doesn't implement an IComparable-like interface
}

如果您不使用 C# 4,您可以查看这些值是否实现IComparable

if (cell1 is IComparable)
{
   int compareResult = ((IComparable)cell1).CompareTo(cell2);
}

或者它实现了一个泛型IComparable<T>,在这种情况下可能需要使用一些反射技巧:

Type genericComparableType = typeof(IComparable<>);
Type typedComparableType = genericComparableType.MakeGenericType(new Type[] { type1 });
if (typedComparableType.IsInstanceOfType(cell1))
{
    MethodInfo compareTo = typedComparableType.GetMethod("CompareTo", new Type[] { type1 });
    int compareResult = (int)compareTo.Invoke(cell1, new object[] { cell2 });
}

最后,您可以Comparer<T>.Default再次使用一些反射来查看是否可行:

Type genericComparerType = typeof(Comparer<>);
Type typedComparerType = genericComparerType.MakeGenericType(new Type[] { type1 });
PropertyInfo defaultProperty = typedComparerType.GetProperty("Default", BindingFlags.Static | BindingFlags.Public);

object defaultComparer = defaultProperty.GetValue(null, null);
MethodInfo compare = defaultComparer.GetType().GetMethod("Compare", new Type[] { type1, type1 });
int compareResult = (int)compare.Invoke(defaultComparer, new object[] { cell1, cell2 });

如果这些都不起作用,那么您将不得不回退到字符串比较。

于 2012-07-03T01:28:36.007 回答
0

转换为适当的类型

Int32 i1 = Int32.Parse(cell1.Tostring());

Int32 i2 = Int32.Parse(cell2.Tostring());

return i1.Compare(i2);
于 2012-07-03T00:27:16.333 回答