通用接口中的协变
Covariance in generic interfaces
我想创建一个可排序的 observableCollection
所以我开始创建一个 class 继承 observable 并使用一些方法对其进行排序,然后我希望 class 将索引持久保存到孩子中,所以我创建了一个公开索引的接口 属性 我可以写入的地方,我将我的集合 class 的 T 约束为我的界面,然后我希望能够从每个项目访问 parent 集合,这里开始出现问题,因为parent 集合的类型是通用的...
我已经尝试了很多解决方案,我认为协方差或不变性是方法,但我无法让它工作...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ClassLibrary1
{
public class SortableCollection<T> : System.Collections.ObjectModel.ObservableCollection<T>, ISortableCollection<T> where T : ISortable<T>
{
public void Sort()
{
//We all know how to sort something
throw new NotImplementedException();
}
protected override void InsertItem(int index, T item)
{
item.Index = index;
item.ParentCollection = this;
base.InsertItem(index, item);
}
}
public interface ISortableCollection<T> : IList<T>
{
void Sort();
}
public interface ISortable<T>
{
Int32 Index { get; set; }
ISortableCollection<T> ParentCollection { get; set; }
}
public class BaseClass : ISortable<BaseClass>
{
public int Index { get; set; }
public ISortableCollection<BaseClass> ParentCollection { get; set; }
}
public class DerivedClass : BaseClass { }
public class Controller
{
SortableCollection<BaseClass> MyBaseSortableList = new SortableCollection<BaseClass>();
SortableCollection<DerivedClass> MyDerivedSortableList = new SortableCollection<DerivedClass>();
public Controller()
{
//do things
}
}
}
这或多或少是设置。
我希望能够创建一个 SortableCollection<DerivedClass>
但类型不匹配...这是正确的方法吗?
确切的错误是
Error 1 The type 'ClassLibrary1.DerivedClass' cannot be used as type parameter 'T' in the generic type or method 'ClassLibrary1.SortableCollection<T>'
. There is no implicit reference conversion from 'ClassLibrary1.DerivedClass' to 'ClassLibrary1.ISortable<ClassLibrary1.DerivedClass>'
. c:\users\luigi.trabacchin\documents\visual studio 2013\Projects\ClassLibrary1\ClassLibrary1\Class1.cs 48 89 ClassLibrary1
您需要 DerivedClass
才能成为 ISortable<DerivedClass>
:
public class DerivedClass : BaseClass, ISortable<DerivedClass>
{
public new ISortableCollection<DerivedClass> ParentCollection
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
}
T
的协变和逆变在这里不起作用,因为您是从不变的 IList<T>
派生的。
即使删除 IList<T>
并删除 getter 我现在也无法正常工作。不完全是我的强项。这是类型系统的一部分,如果您能提供帮助,最好不要管它。
如果类型系统让你的脑袋爆炸,请考虑动态解决方案:
((dynamic))item).ParentCollection = this;
问题是您对 T
的约束是“T
必须是 I<T>
”,并且您已经为 [=10] 通过了 DerivedClass
=],但是 DerivedClass
不能转换为 I<DerivedClass>
,它可以转换为 I<BaseClass>
。
我不知道你试图用 T
是 I<T>
的约束来表示什么。我知道人们经常使用这种模式来尝试表示 C# 类型系统实际上没有实现的约束。有关详细信息,请参阅我关于该主题的文章:
http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx
我鼓励你大大简化事情;您似乎试图在类型系统中捕获太多内容。
I<D>
不能转换为 I<B>
的原因是为了使变体起作用,必须将接口标记为支持变体;根据您想要协变还是逆变,将 T
标记为 out
或 in
。
但是,由于 IList<T>
是不变的,因此使派生接口协变或逆变都是不合法的。请考虑 IEnumerable<T>
,因为它在 T
.
中是协变的
为了使接口在T
中是协变的,它只需要在output 位置使用T
。 List<T>
在输入和输出位置都使用T
,所以不能协变也不能逆变。
为了感谢大家,我将post我最终得到的设计
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ClassLibrary1
{
public class SortableCollection<T> : System.Collections.ObjectModel.ObservableCollection<T>, ISortableCollection where T : ISortable, IComparable, IComparable<T>
{
public new void Add(T item)
{
if (this.Items.Contains(item))
throw new InvalidOperationException("This list can contain the same item only once");
base.Add(item);
}
public void Sort()
{
var sorted = this.Items.ToList();
sorted.Sort();
for (var i = 0; i < this.Items.Count; i++)
{
if (object.ReferenceEquals(this.Items[i], sorted[i]))
{
this.Items[i].Index = i;
continue;
}
// if u want to support duplicates create a nextIndexOf and start searching from i
var previousIndex = IndexOf(sorted[i]);
Move(previousIndex, i);
}
}
protected override void InsertItem(int index, T item)
{
item.Index = index;
item.ParentCollection = this;
base.InsertItem(index, item);
}
protected override void RemoveItem(int index)
{
this.Items[index].ParentCollection = null;
base.RemoveItem(index);
}
protected override void ClearItems()
{
foreach (var item in this.Items)
item.ParentCollection = null;
base.ClearItems();
}
protected override void SetItem(int index, T item)
{
this.Items[index].ParentCollection = null;
item.Index = index;
item.ParentCollection = this;
base.SetItem(index, item);
}
protected override void MoveItem(int oldIndex, int newIndex)
{
this.Items[oldIndex].Index = newIndex;
this.Items[newIndex].Index = oldIndex;
base.MoveItem(oldIndex, newIndex);
}
}
public interface ISortableCollection : IList
{
void Sort();
}
public interface ISortable
{
Int32 Index { get; set; }
ISortableCollection ParentCollection { get; set; }
}
public class BaseClass : ISortable, IComparable, IComparable<BaseClass>
{
public int Index { get; set; }
public ISortableCollection ParentCollection { get; set; }
public int CompareTo(object obj)
{
return CompareTo(obj as BaseClass);
}
public int CompareTo(BaseClass other)
{
if (other == null)
return 1;
return this.Index.CompareTo(other.Index);
}
}
public class DerivedClass : BaseClass { }
public class Controller
{
SortableCollection<BaseClass> MyBaseSortableList = new SortableCollection<BaseClass>();
SortableCollection<DerivedClass> MyDerivedSortableList = new SortableCollection<DerivedClass>();
public Controller()
{
//do things
MyDerivedSortableList.Add(new DerivedClass());
MyDerivedSortableList.Add(new DerivedClass());
var derivedThing = new DerivedClass();
MyDerivedSortableList.Add(derivedThing);
var sibiling = derivedThing.ParentCollection[derivedThing.Index - 1] as BaseClass; //way easier
// switch the two objects order and call sort
// calling a sort before the operation if indexes have been messed with
// add an event to ISortable to notify the list the index has been changed and mark the list dirty
derivedThing.Index -= 1;
sibiling.Index += 1;
derivedThing.ParentCollection.Sort(); // maybe the list was created where i couldn't access it
}
}
}
我想创建一个可排序的 observableCollection 所以我开始创建一个 class 继承 observable 并使用一些方法对其进行排序,然后我希望 class 将索引持久保存到孩子中,所以我创建了一个公开索引的接口 属性 我可以写入的地方,我将我的集合 class 的 T 约束为我的界面,然后我希望能够从每个项目访问 parent 集合,这里开始出现问题,因为parent 集合的类型是通用的... 我已经尝试了很多解决方案,我认为协方差或不变性是方法,但我无法让它工作...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ClassLibrary1
{
public class SortableCollection<T> : System.Collections.ObjectModel.ObservableCollection<T>, ISortableCollection<T> where T : ISortable<T>
{
public void Sort()
{
//We all know how to sort something
throw new NotImplementedException();
}
protected override void InsertItem(int index, T item)
{
item.Index = index;
item.ParentCollection = this;
base.InsertItem(index, item);
}
}
public interface ISortableCollection<T> : IList<T>
{
void Sort();
}
public interface ISortable<T>
{
Int32 Index { get; set; }
ISortableCollection<T> ParentCollection { get; set; }
}
public class BaseClass : ISortable<BaseClass>
{
public int Index { get; set; }
public ISortableCollection<BaseClass> ParentCollection { get; set; }
}
public class DerivedClass : BaseClass { }
public class Controller
{
SortableCollection<BaseClass> MyBaseSortableList = new SortableCollection<BaseClass>();
SortableCollection<DerivedClass> MyDerivedSortableList = new SortableCollection<DerivedClass>();
public Controller()
{
//do things
}
}
}
这或多或少是设置。
我希望能够创建一个 SortableCollection<DerivedClass>
但类型不匹配...这是正确的方法吗?
确切的错误是
Error 1 The type 'ClassLibrary1.DerivedClass' cannot be used as type parameter 'T' in the generic type or method
'ClassLibrary1.SortableCollection<T>'
. There is no implicit reference conversion from 'ClassLibrary1.DerivedClass' to'ClassLibrary1.ISortable<ClassLibrary1.DerivedClass>'
. c:\users\luigi.trabacchin\documents\visual studio 2013\Projects\ClassLibrary1\ClassLibrary1\Class1.cs 48 89 ClassLibrary1
您需要 DerivedClass
才能成为 ISortable<DerivedClass>
:
public class DerivedClass : BaseClass, ISortable<DerivedClass>
{
public new ISortableCollection<DerivedClass> ParentCollection
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
}
T
的协变和逆变在这里不起作用,因为您是从不变的 IList<T>
派生的。
即使删除 IList<T>
并删除 getter 我现在也无法正常工作。不完全是我的强项。这是类型系统的一部分,如果您能提供帮助,最好不要管它。
如果类型系统让你的脑袋爆炸,请考虑动态解决方案:
((dynamic))item).ParentCollection = this;
问题是您对 T
的约束是“T
必须是 I<T>
”,并且您已经为 [=10] 通过了 DerivedClass
=],但是 DerivedClass
不能转换为 I<DerivedClass>
,它可以转换为 I<BaseClass>
。
我不知道你试图用 T
是 I<T>
的约束来表示什么。我知道人们经常使用这种模式来尝试表示 C# 类型系统实际上没有实现的约束。有关详细信息,请参阅我关于该主题的文章:
http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx
我鼓励你大大简化事情;您似乎试图在类型系统中捕获太多内容。
I<D>
不能转换为 I<B>
的原因是为了使变体起作用,必须将接口标记为支持变体;根据您想要协变还是逆变,将 T
标记为 out
或 in
。
但是,由于 IList<T>
是不变的,因此使派生接口协变或逆变都是不合法的。请考虑 IEnumerable<T>
,因为它在 T
.
为了使接口在T
中是协变的,它只需要在output 位置使用T
。 List<T>
在输入和输出位置都使用T
,所以不能协变也不能逆变。
为了感谢大家,我将post我最终得到的设计
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ClassLibrary1
{
public class SortableCollection<T> : System.Collections.ObjectModel.ObservableCollection<T>, ISortableCollection where T : ISortable, IComparable, IComparable<T>
{
public new void Add(T item)
{
if (this.Items.Contains(item))
throw new InvalidOperationException("This list can contain the same item only once");
base.Add(item);
}
public void Sort()
{
var sorted = this.Items.ToList();
sorted.Sort();
for (var i = 0; i < this.Items.Count; i++)
{
if (object.ReferenceEquals(this.Items[i], sorted[i]))
{
this.Items[i].Index = i;
continue;
}
// if u want to support duplicates create a nextIndexOf and start searching from i
var previousIndex = IndexOf(sorted[i]);
Move(previousIndex, i);
}
}
protected override void InsertItem(int index, T item)
{
item.Index = index;
item.ParentCollection = this;
base.InsertItem(index, item);
}
protected override void RemoveItem(int index)
{
this.Items[index].ParentCollection = null;
base.RemoveItem(index);
}
protected override void ClearItems()
{
foreach (var item in this.Items)
item.ParentCollection = null;
base.ClearItems();
}
protected override void SetItem(int index, T item)
{
this.Items[index].ParentCollection = null;
item.Index = index;
item.ParentCollection = this;
base.SetItem(index, item);
}
protected override void MoveItem(int oldIndex, int newIndex)
{
this.Items[oldIndex].Index = newIndex;
this.Items[newIndex].Index = oldIndex;
base.MoveItem(oldIndex, newIndex);
}
}
public interface ISortableCollection : IList
{
void Sort();
}
public interface ISortable
{
Int32 Index { get; set; }
ISortableCollection ParentCollection { get; set; }
}
public class BaseClass : ISortable, IComparable, IComparable<BaseClass>
{
public int Index { get; set; }
public ISortableCollection ParentCollection { get; set; }
public int CompareTo(object obj)
{
return CompareTo(obj as BaseClass);
}
public int CompareTo(BaseClass other)
{
if (other == null)
return 1;
return this.Index.CompareTo(other.Index);
}
}
public class DerivedClass : BaseClass { }
public class Controller
{
SortableCollection<BaseClass> MyBaseSortableList = new SortableCollection<BaseClass>();
SortableCollection<DerivedClass> MyDerivedSortableList = new SortableCollection<DerivedClass>();
public Controller()
{
//do things
MyDerivedSortableList.Add(new DerivedClass());
MyDerivedSortableList.Add(new DerivedClass());
var derivedThing = new DerivedClass();
MyDerivedSortableList.Add(derivedThing);
var sibiling = derivedThing.ParentCollection[derivedThing.Index - 1] as BaseClass; //way easier
// switch the two objects order and call sort
// calling a sort before the operation if indexes have been messed with
// add an event to ISortable to notify the list the index has been changed and mark the list dirty
derivedThing.Index -= 1;
sibiling.Index += 1;
derivedThing.ParentCollection.Sort(); // maybe the list was created where i couldn't access it
}
}
}