【问题标题】:unable use compare to with two different generic object with different T types无法与具有不同 T 类型的两个不同泛型对象进行比较
【发布时间】:2016-06-15 21:49:17
【问题描述】:

我有两种类型都扩展了一个通用的基本类型。这两种类型都没有额外的属性,只为抽象值赋值。当我尝试使用CompareTo 比较这两种类型时,我得到一个异常堆栈跟踪一英里长,它源于一个失败的类型案例。

如果我尝试使用泛型进行强制转换,是否可以比较这两种类型?

代码如下:

起始类型:

public interface IIdentifier : IEquatable<IIdentifier>, IComparable<IIdentifier>
{
    /// <summary>
    /// Numerical identifier
    /// </summary>
    long Id { get; }

    /// <summary>
    /// Provide the type of identifier.
    /// </summary>
    IdentifierType IdType { get; }
}

接下来两种类型扩展的基本标识符类型:

[DataContract]
public abstract class BaseIdentifier<T> : IIdentifier, IComparable<BaseIdentifier<T>>, IComparable, IEquatable<BaseIdentifier<T>> where T : class, IIdentifier
{
    #region Properties

    [DataMember(Order = 1)]
    public long Id { get; set; }

    [IgnoreDataMember]
    public abstract IdentifierType IdType { get; }

    #endregion
    #region Constructors

    protected BaseIdentifier(long id)
    {
        Id = id;
    }

    protected BaseIdentifier()
    {
    }

    #endregion

    #region IEquatable<IIdentifier> Members

    public bool Equals(IIdentifier other)
    {
        if (ReferenceEquals(other, null))
            return false;

        return this.IdType == other.IdType && this.Id == other.Id;
    }

    #endregion

    #region IComparable<IIdentifier> Members

    public int CompareTo(IIdentifier other)
    {
        int c = this.IdType.CompareTo(other.IdType);
        if (c != 0)
            c = this.Id.CompareTo(other.Id);
        return c;
    }

    #endregion
    #region IComparable<BaseIdentifier<T>> Members

    public int CompareTo(BaseIdentifier<T> other)
    {
        return Id.CompareTo(other.Id);
    }

    #endregion

    #region IComparable

    public int CompareTo(object obj)
    {
        return CompareTo(obj as BaseIdentifier<T>);
    }

    #endregion

    #region IEquatable<BaseIdentifier<T>> Members

    public bool Equals(BaseIdentifier<T> other)
    {
        if (ReferenceEquals(other, null))
            return false;

        return this.Id == other.Id;
    }

    #endregion

}

以下是使用BaseIdentifier&lt;T&gt; 函数尝试CompareTo 时抛出异常的两种类型。

[DataContract]
public class Type1 : BaseIdentifier<Type1>
{
    [IgnoreDataMember]
    public override IdentifierType IdType
    {
        get { return IdentifierType.Type1; }
    }

    public Type1(long Id)
        : base(Id)
    {
    }

    public Type1()
    {
        // For serialization
    }
}

[DataContract]
public class Type2 : BaseIdentifier<Type2>
{
    [IgnoreDataMember]
    public override IdentifierType IdType
    {
        get { return IdentifierType.Type2; }
    }

    public Type1(long Id)
        : base(Id)
    {
    }

    public Type2()
    {
        // For serialization
    }
}

例外:

"Unable to cast object of type 'Domain.Contracts.Type2' 
to type 'Domain.Contracts.Type1`1[Domain.Contracts.Type1]'.""   
at Domain.Contracts.BaseIdentifier`1.CompareTo(Object obj) in 
C:\\xsr\\path\\BaseIdentifier.cs:line 109\r\n   
at Xceed.Utils.Data.ObjectDataStore.CompareData(Object xData, Object yData)\r\n   
at Xceed.Utils.Data.ObjectDataStore.Compare(Int32 xRecordIndex, Int32 yRecordIndex)\r\n   
at Xceed.Wpf.DataGrid.DataGridCollectionViewSort.Compare(Int32 xDataIndex, Int32 yDataIndex)\r\n   
at Xceed.Utils.Collections.IndexWeakHeapSort.Sort(Int32 length)\r\n   
at Xceed.Wpf.DataGrid.DataGridCollectionViewGroupRoot.SortRootRawItems(SortDescriptionInfo[] sortDescriptionInfos, List`1 globalRawItems)\r\n   
at Xceed.Wpf.DataGrid.DataGridCollectionView.SortItems(SortDescriptionInfo[] sortDescriptionInfos)\r\n   
at Xceed.Wpf.DataGrid.DataGridCollectionView.ExecuteSourceItemOperation(DeferredOperation deferredOperation, Boolean& refreshForced)\r\n   
at Xceed.Wpf.DataGrid.DeferredOperationManager.Process(Boolean processAll)\r\n   
at Xceed.Wpf.DataGrid.DataGridCollectionViewBase.DeferRefreshHelper.ProcessDispose(DataGridCollectionViewBase collectionView)\r\n   
at Xceed.Wpf.DataGrid.DataGridCollectionViewBase.DeferRefreshHelper.Dispose(Boolean disposing)\r\n   
at Xceed.Wpf.DataGrid.DataGridCollectionViewBase.DeferRefreshHelper.System.IDisposable.Dispose()\r\n   
at Xceed.Wpf.DataGrid.DataGridSortDescriptionCollection.DeferResortDisposable.Dispose()\r\n   
at Xceed.Wpf.DataGrid.ColumnSortCommand.Disposer.Dispose(Boolean disposing)"

【问题讨论】:

  • 什么是exact异常?如果堆栈跟踪是“一英里长”,听起来您可能有无限递归。你能在调试器中运行它并在CompareTo方法中中断吗?
  • 这是一英里长,因为它试图在第三方网格系统的比较中从空类型中获取属性。所以第三方网格系统是导致异常如此长的原因。查看异常的更新

标签: c# generics comparison


【解决方案1】:

您的CompareTo(ojbect) 代码试图将BaseIdentifier&lt;Type1&gt; 转换为BaseIdentifier&lt;Type1&gt;,这是不合法的。

由于您无法比较具有不同泛型参数的对象,因此您似乎应该更改

public int CompareTo(object obj)
{
    return CompareTo(obj as BaseIdentifier<T>);
}

public int CompareTo(object obj)
{
    return CompareTo(obj as IIdentifier);
}

这样您就可以绑定到采用 接口 而不是泛型类型的重载。

您还可以考虑重载Equals(object),将其重定向到Equals(IIdentifier),这意味着您需要重载GetHashCode

【讨论】:

  • 是的,我已经绑定了这个,它确实有效。我只是不知道我是否做错了什么可以纠正参数版本的工作。
  • 我已经编辑了这个问题(希望)解释为什么它不起作用。我没有看到任何其他明显的你失踪的东西。您只需要确保将重载绑定到正确的基本方法。
【解决方案2】:

这与泛型在 C# 中的工作方式有关。泛型语法实际上只是为了让您的生活更轻松,但您必须记住,更改泛型类型确实会使其成为不同的类型。所以BaseIdentifier&lt;Type1&gt; 是与BaseIdentifier&lt;Type2&gt; 完全不同的类型。您在代码中编写一次,但在编译时将它们分开。

弄清楚如何准确地实现您的目标可能很复杂。根据您上面的示例,最简单的选择是使其实现 IComparible&lt;IIdentifier&gt; 而不是使用泛型类型。

【讨论】:

  • 你是说只实现IComparible 而不是IComparible&lt;T&gt;
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-24
  • 2019-08-06
  • 2012-09-07
相关资源
最近更新 更多