这两者之间的区别是什么,“正确”的是什么?
public interface IMessage
{
/// <summary>
/// Array used to hold all bytes that will be written.
/// </summary>
IList Buffer { get; set; }
}
和
public interface IMessage<T> where T : IList
{
/// <summary>
/// Array used to hold all bytes that will be written.
/// </summary>
T Buffer { get; set; }
}
编辑1:已修复 - 界面上不能包含字段。 (谢谢BoltClock)
编辑2:已修复 - 无法在接口上进行封装。 (谢谢KeithS)
答案 0 :(得分:5)
两者在概念上都是“正确的”,并且在语义上意味着几乎相同的事情(如果一个人不介意语法错误 - 因为接口不能有字段,如果将它们定义为属性,这将没有问题。)
通用版本允许您返回IList
以外的类型 - 因此可以返回List
而不是接口类型。
答案 1 :(得分:0)
如果我没记错的话,在第二个版本中,您可以使用IList自己的类。
答案 2 :(得分:0)
在第一种情况下,您的缓冲区被定义为IList类型。它可以在运行时分配IList的任何实现,并且此接口的任何实现创建者都不必确切知道将使用哪种实现。但是,代码在运行时无法知道使用了哪个具体实现,因此在设计时将此属性作为接口的成员引用时,您将永远无法访问IList接口未明确公开的任何方法。
在第二种情况下,您正在定义泛型。只要此通用定义保持“开放”(T未定义),您仍然只能使用IList的方法。但是,某些东西必须关闭这个泛型,要么是接口的实现,要么是保持泛型开放的实现的特定实例的定义。一旦通用关闭,缓冲区的确切,具体类型已知,并且可以在其上调用不一定由IList定义的方法。但是,一旦通用关闭,定义特定泛型类型的实例或类就不能被赋予IList的不同实现来用作缓冲区。
因此,一些示例(假设您在接口上无法指定的受保护字段实际上是可变公共属性):
//this class implements the non-generic interface, so buffer is an IList.
class MyMessage1: IMessage
{
public IList buffer {get;set;}
public MyMessage1()
{
buffer = new List<string>();
//even though you "know" what you just assigned,
//you cannot refer to buffer as a List<string>, even here.
buffer.Sort(); //error
}
}
...
//The exact type of buffer cannot be known statically,
//so only non-generic IList methods are allowed
var myMessage = new MyMessage1();
myMessage.buffer.Add("my message"); //valid; string literals are Objects
var firstLen = myMessage.buffer[0].Length; //error: indexer returns Objects.
myMessage.Sort(); //error: IList does not have a Sort() method.
firstLen = GetFirstLength(myMessage); //error: not an IMessage<List<string>>
//but, an IList is an IList no matter what, so this works.
myMessage.buffer = new List<int>();
...
//this class keeps the generic open so T can be any IList, determined at instantiation.
class MyMessage2<T>:IMessage<T> where T:IList
{
public T buffer {get;set;}
//buffer's exact type is still not known here,
//so inside this class you are still restricted to IList members only
public int BufferCount{get{return buffer.Count;}}
public void SortBuffer()
{
buffer.Sort(); //error; no such method
}
}
...
//but, once you define an instance, you know exactly what buffer is
var myMessage = new MyMessage2<List<string>>();
myMessage.buffer.Add("my message");
var firstLen = myMessage.buffer[0].Length; //now we know the indexer produces strings.
myMessage.buffer.Sort(); //buffer is known to be a List<T> which has Sort()
firstLen = GetFirstLength(myMessage);
...
//and when you pass it as a parameter, you can close the generic of the interface
public string GetFirstLength(IMessage<List<string>> message)
{
//...so you still know what you're dealing with
return message.buffer[0].Length;
}
...
//however, buffer is now "strongly typed" and the implementation can't change
myMessage.buffer = new List<int>(); //error; buffer is of type List<string>
...
//this class closes the generic within the declaration.
class MyMessage3:IMessage<IList<string>>
{
//now we're closing the generic in the implementation itself,
//so internally we know exactly what we're dealing with
public List<string> buffer {get;set;}
//...so this call is valid
public void SortBuffer() { buffer.Sort(); }
}
//...and consuming code doesn't have to (get to?) specify the implementation of T
var myMessage = new MyMessage3();
//... but still knows exactly what that implementation is
myMessage.buffer.Add("my message");
var firstLen = myMessage.buffer[0].Length;
myMessage.buffer.Sort();
//and btw, MyMessage3 is still an IMessage<List<string>>
firstLen = GetFirstLength(myMessage);
//... and buffer's still a strongly-typed List<string>
myMessage.buffer = new List<int>(); //error