这个派生接口getter有什么不明确之处

本文关键字:什么 不明确 getter 派生 接口 | 更新日期: 2023-09-27 18:18:48

我希望添加这些接口:

public interface IIndexGettable<TKey, TValue>
{
    TValue this[TKey key]{ get; }
}
public interface IIndexSettable<TKey, TValue>
{
    TValue this[TKey key]{ set; }
}
public interface IIndexable<TKey, TValue> : IIndexGettable<TKey, TValue>, IIndexSettable<TKey, TValue>
{   
  // new TValue this[TKey key]{ get; set; } // as suggested by SO question 1791359 this will "fix" the issue.
}

当我尝试使用IIndexable的getter时,我得到以下编译器错误:

The call is ambiguous between the following methods or properties: 'IIndexGettalbe<TKey, TValue>.this[Tkey]' and 'IIndexSettable<TKey, TValue>.this[TKey]'

下面是我运行的代码:

public static class IIndexableExtensions
 {
    public static Nullable<TValue> AsNullableStruct<TKey, TValue>(this IIndexable<TKey, object> reader, TKey keyName) where TValue : struct
    {
        if (reader[keyName] == DBNull.Value) // error is on this line.
        {
            return null;
        }
        else
        {
            return (TValue)reader[keyName];
        }
    }   
 }

它不应该明确地继承IIndexGettable的getter和IIndexSettable的setter吗?

顺便说一下,我并不想让这种声音对语言设计产生煽动性。我相信这背后有一个很好的理由,我的目标是了解原因是什么,以便更好地理解这门语言。

这个派生接口getter有什么不明确之处

尝试下面的代码,它工作得很好。你的错误是什么情况?这段代码中没有什么?

class Program
    {
        static void Main(string[] args)
        {
            Foo<int, string> foo = new Foo<int, string>();
            foo.dict.Add(1, "a");
            foo.dict.Add(2, "b");
            Console.WriteLine(((IIndexGettable<int, string>)foo)[1]);
            ((IIndexSettable<int, string>)foo)[3] = "c";
            Console.WriteLine(foo[3]);
            Console.ReadLine();
        }
    }
    public interface IIndexGettable<TKey, TValue>
    {
        TValue this[TKey key] { get; }
    }
    public interface IIndexSettable<TKey, TValue>
    {
        TValue this[TKey key] { set; }
    }
    public interface IIndexable<TKey, TValue> : IIndexGettable<TKey, TValue>, IIndexSettable<TKey, TValue>
    {
    }
    public class Foo<TKey, TValue> : IIndexable<TKey, TValue>
    {
        public Dictionary<TKey, TValue> dict = new Dictionary<TKey, TValue>();
        public TValue this[TKey key]
        {
            get
            {
                return dict[key];
            }
            set
            {
                dict[key] = value;
            }
        }
    }

回答你的问题"这个派生的接口getter有什么不明确的地方?"

定义属性(参数性属性/访问器)的类型。

似乎编译器会首先检查定义属性的类型&然后如果它是Getter或Setter。

这里我们定义了一个同名的属性&在两个接口中签名,这就是歧义。它不会给编译器一个进入下一步的改变,也不会检查它是getter还是setter

这是我做的一个简短的实验来观察它在智力上的行为-

我做了一个更简单的设置-

public interface IIndexGettable
{
    int Index { get; }
}
public interface IIndexSettable
{
    int Index { set; }
}
 public interface IIndexable : IIndexGettable, IIndexSettable
 {
 }

现在我用智能来实现接口IIndexable

问题就在这里

public class ConcreteIndexable : IIndexable
 {
     public int Index
     {
         get { throw new NotImplementedException(); }
     }
     int IIndexSettable.Index
     {
         set { throw new NotImplementedException(); }
     }
 }

它将int IIndexSettable.Index实现为显式接口实现,就像在两个接口具有相同功能的情况下一样。

现在,如果两个接口中有两个函数具有相同的名称&签名。但是正如我们所看到的,它也处理了一个只有get的属性和一个只有set的属性,就像它们完全一样,并创建了一个显式的接口实现。这就是我想要表达的观点。如果您查看自动生成的代码,它并不适合—一个显式接口实现,与set

正如您在问题中指出的那样,添加new关键字将解决此问题。但是一个更好的方法是:

public interface IReadOnlyIndexable<TKey, TValue> : IEnumerable<TKey, TValue>
{
    TValue this[TKey key]{ get; }
    int Count { get; }
}
public interface IIndexable<TKey, TValue> : IReadOnlyIndexable<TKey, TValue>
{
    new TValue this[TKey key]{ get; set; }
}

一件事是,你很少(如果有的话)想要一个没有getter的属性setter(我从来没有使用过它,从来没有在BCL或任何知名的库中看到过它)。如果你只使用setter属性,很可能意味着你做错了什么。

这允许你做OOP应该让你做的事情:将IIndexable<T, V>的实例传递给接受IReadOnlyIndexable<TKey, TValue>的方法,并确保它们不能改变它。

。NET BCL,从。NET 4.5开始,有一个不同的方法;它定义了IList<T>/IReadOnlyList<T>IDictionary<T, V>/IReadOnlyDictionary<T, V>,但是可变接口不能从只读接口继承。相反,BCL类实现了两个接口:

public class List<T> : IList<T>, IReadOnlyList<T> ...
public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>,
      IReadOnlyDictionary<TKey, TValue>, ...

我更喜欢第一种(继承)方法,因为它允许通过其可变接口引用实例的方法将实例传递给需要不可变实例的方法(这在BCL类中是不能做到的):

public void ReadStuff(IReadOnlyIndexable<T, K> instance) 
{
    // read from instance
}
public void ReadOrWrite(IIndexable<T, K> instance)
{
    // write to instance
    ReadStuff(instance); // and you can simply pass it as IReadOnlyIndexable
}
顺便说一句,几年前我在这篇文章中做过类似的事情,尽管它只是一个列表/集合的接口。但这篇文章背后的想法可能与您的相似:我希望能够将集合包装在一个轻量级的只读包装器中,这样我就可以将其传递出去,而不必担心我的某些代码会意外地改变它。我可以添加很酷的类似linq的扩展方法,这将允许我包装我的集合片段,并将它们作为不可变的传递。