【问题标题】:Xamarin.Android: item previously inserted in ArrayAdapter is not found againXamarin.Android:之前插入 ArrayAdapter 的项目未再次找到
【发布时间】:2015-02-26 06:19:03
【问题描述】:

我继承了这个 Xamarin.Android 应用,但它存在一些问题。

一个特定的错误涉及ArrayAdapter<ProductListObject>,其中ProductListObject 是子项目(即Android、Windows Phone 和iOS)之间共享的常见POCO;它只有几个属性(例如Id)并覆盖(.NET)Equals() 方法以实现结构平等:

public class ProductListObject
{
    public long Id { get; set; }

    public override bool Equals(object obj)
    {
        if (!(obj is ProductListObject))
        {
            return false;
        }

        return Id == (obj as ProductListObject).Id;
    }
}

问题是,每当我将这个ProductListObject 的实例放入ArrayAdapter 中时,即使它们具有相同的Id,我也无法再次找到它:

var p1 = new ProductListObject { Id = 1 };
var p2 = new ProductListObject { Id = 1 };

var areEqual = p1.Equals(p2); // returns True, as expected

var productAdapter = new ArrayAdapter<ProductListObject>(this, 0, new[] { p1 });
var position = productAdapter.GetPosition(p2); // returns -1 >:(

我的问题是:我必须做些什么才能使我的 POCO 与内部 (like ArrayAdapter; which delegates to List.indexOf(Object)) 依赖于 Java equals() 方法的 Xamarin.Android 类型一起工作?

我尝试过的:

  • 已验证相应的 Java 版本是否按预期工作(确实如此)
  • 覆盖GetHashCode()(没关系,正如我所料)
  • 谷歌搜索并检查了 Xamarin 文档以获取有关实现 Equals() 的信息(我没有发现任何特别相关的内容)

谢谢, 一月

【问题讨论】:

    标签: xamarin xamarin.android android-arrayadapter


    【解决方案1】:

    我按照 Matt R 的建议做了,并创建了一个继承自 Java.Lang.Object 并委托给实际 .NET 对象的代理:

    public class JavaObject<TValue> : Java.Lang.Object
    {
        public readonly TValue Value;
    
        internal JavaObject(TValue value)
        {
            Value = value;
        }
    
        public override bool Equals(Java.Lang.Object that)
        {
            if (!(that is JavaObject<TValue>))
            {
                return false;
            }
    
            return Value.Equals((that as JavaObject<TValue>).Value);
        }
    
        public override int GetHashCode()
        {
            return Value.GetHashCode();
        }
    }
    

    这不会将我与平台无关的 POCO 与 Android 实现联系起来,而且它不会强迫我锁定某些严格的继承树,这总是一个优点。

    应用很简单:

    var p1 = new JavaObject<ProductListObject>(new ProductListObject { Id = 1 });
    var p2 = new JavaObject<ProductListObject>(new ProductListObject { Id = 1 });
    
    var areEqual = p1.Equals(p2); // returns True, as expected
    
    var productAdapter = new ArrayAdapter<JavaObject<ProductListObject>>(this, 0, new[] { p1 });
    var position = productAdapter.GetPosition(p2); // returns 0!
    

    【讨论】:

      【解决方案2】:

      看起来 .NET 对象在 Android.Widget.ArrayAdapter 中使用时被包装在 Java.Lang.Object 中。因此,productAdapter.GetPosition(...) 调用中使用的比较方法实际上是用于包装 Java.Lang.Object 的 java Equals(Java.Lang.Object o) 方法。

      当两个对象具有相同的Id 时,要使ProductListObject 解析为相同的索引,请使ProductListObject 派生自Java.Lang.Object,覆盖Equals(Java.Lang.Object) 并将其转发给.NET 的Equals(System.Object) 方法:

      public class ProductListObject : Java.Lang.Object
      {
          public long Id { get; set; }
      
          public override bool Equals(object obj) // Inherited from System.Object.
          {
              if (!(obj is ProductListObject))
              {
                  return false;
              }
      
              return Id == (obj as ProductListObject).Id;
          }
      
          public override bool Equals (Java.Lang.Object o) // Inherited from Java.Lang.Object.
          {
              return this.Equals (o as System.Object);
          }
      }
      

      如果你不能从Java.Lang.Object继承ProductListObject,另一种选择是实现你自己的代理类:

      public class ProductListObject
      {
          public long Id { get; set; }
      
          public override bool Equals(System.Object obj)
          {
              if (!(obj is ProductListObject))
              {
                  return false;
              }
      
              return Id == (obj as ProductListObject).Id;
          }
      }
      
      public class JavaProxy: Java.Lang.Object 
      {
          public Object Object { get; private set; } 
      
          public JavaProxy(System.Object o)
          {
              Object = o;
          }
      
          public override bool Equals (Java.Lang.Object o)
          {
              var proxy = o as JavaProxy;
              if (o != null) {
                  return Object.Equals (proxy.Object);
              }
              return base.Equals (o);
          }
      }
      
      // ...
      
      var productAdapter = new ArrayAdapter<JavaProxy>(this, 0, new[] { new JavaProxy(p1) });
      var position = productAdapter.GetPosition(new JavaProxy(p2)); 
      

      它不像第一种方法那样干净,但它也有效。

      【讨论】:

      • 是的,但这会将我的 POCO(也用于 Windows Phone 和 iOS 子项目)绑定到 Xamarin.Android。
      • 这是一个 Xamarin.Forms 应用吗?如果没有,另一种选择是创建自己的 Java.Lang.Object 代理,将其 equals 方法转发给 .NET 对象的 equals 方法。
      猜你喜欢
      • 1970-01-01
      • 2020-08-27
      • 1970-01-01
      • 1970-01-01
      • 2011-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多