这个派生的接口getter有什么含糊之处?

时间:2015-10-22 17:34:14

标签: c# inheritance interface

我希望添加这些界面:

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吗?

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

3 个答案:

答案 0 :(得分:2)

尝试以下代码,它运行正常。您的错误是什么情况?这段代码中没有什么?

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;
            }
        }

    }

答案 1 :(得分:1)

回答你的问题“这个衍生界面getter有什么含糊之处?”

定义属性(Parameterful property / accessor)的类型。

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

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

这是一个简短的实验,我也是为了观察它在intellisence上的行为 -

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

public interface IIndexGettable
{
    int Index { get; }
}

public interface IIndexSettable
{
    int Index { set; }
}

 public interface IIndexable : IIndexGettable, IIndexSettable
 {

 }

我现在使用intellisence来实现接口IIndexable

这是问题

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

     int IIndexSettable.Index
     {
         set { throw new NotImplementedException(); }
     }
 }

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

现在,如果两个具有相同名称和接口的接口中有2个功能,则此自动生成的代码将具有完美的意义。签名。但正如我们所看到的,它还处理了一个只有get的属性和一个只有set的属性,就像它们完全相同,并创建了一个显式的接口实现。这就是我想传达的观点。如果你看一下自动生成的代码,它就不适合 - 一个显式的接口实现,set

答案 2 :(得分:0)

正如您在问题中所述,添加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
}

顺便说一句,几年前我在this article做了类似的事情,虽然它只是列表/集合的接口。但是这篇文章背后的想法可能与你的相似:我希望能够在一个轻量级的只读包装器中包装集合,我可以传递它而不用担心我的代码的某些部分是否会意外地改变它。我可以添加很酷的LINQ类扩展方法,这些方法允许我包装我的集合的片段并将它们作为不可变的传递。