回顾一个关于SO 的早期问题,我开始考虑一个类暴露一个值(例如集合)作为由值的类型实现的接口的情况.在下面的代码示例中,我使用List,并将该列表公开为IEnumerable.
通过IEnumerable接口公开列表定义了列表仅被枚举,而不是被修改的意图.但是,由于实例可以重新转换回列表,因此列表本身当然可以修改.
我还在示例中包含了一个方法版本,该方法通过在每次调用方法时将列表项引用复制到新列表来阻止修改,从而防止更改基础列表.
所以我的问题是,如果所有代码都将具体类型暴露为已实现的接口,是否应通过复制操作来实现?语言结构中是否有值明确指出"我想通过接口公开此值,并且调用代码应该只能通过接口使用此值"?当通过接口暴露具体值时,其他人使用什么技术来防止这些意外的副作用.
请注意,我了解所示的行为是预期的行为.我没有声称这种行为是错误的,只是它确实允许使用除表达意图之外的功能.也许我给界面赋予了太多的意义 - 将其视为功能约束.思考?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TypeCastTest
{
class Program
{
static void Main(string[] args)
{
// Demonstrate casting situation
Automobile castAuto = new Automobile();
List<string> doorNamesCast = (List<string>)castAuto.GetDoorNamesUsingCast();
doorNamesCast.Add("Spare Tire");
// Would prefer this prints 4 names,
// actually prints 5 because IEnumerable<string>
// was cast back to List<string>, exposing the
// Add method of the underlying List object
// Since the list was cast to IEnumerable before being
// returned, the expressed intent is that calling code
// should only be able to enumerate over the collection,
// not modify it.
foreach (string doorName in castAuto.GetDoorNamesUsingCast())
{
Console.WriteLine(doorName);
}
Console.WriteLine();
// --------------------------------------
// Demonstrate casting defense
Automobile copyAuto = new Automobile();
List<string> doorNamesCopy = (List<string>)copyAuto.GetDoorNamesUsingCopy();
doorNamesCopy.Add("Spare Tire");
// This returns only 4 names,
// because the IEnumerable<string> that is
// returned is from a copied List<string>, so
// calling the Add method of the List object does
// not modify the underlying collection
foreach (string doorName in copyAuto.GetDoorNamesUsingCopy())
{
Console.WriteLine(doorName);
}
Console.ReadLine();
}
}
public class Automobile
{
private List<string> doors = new List<string>();
public Automobile()
{
doors.Add("Driver Front");
doors.Add("Passenger Front");
doors.Add("Driver Rear");
doors.Add("Passenger Rear");
}
public IEnumerable<string> GetDoorNamesUsingCopy()
{
return new List<string>(doors).AsEnumerable<string>();
}
public IEnumerable<string> GetDoorNamesUsingCast()
{
return doors.AsEnumerable<string>();
}
}
}
Run Code Online (Sandbox Code Playgroud)
一种可以防止这种情况的方法是使用AsReadOnly()来防止任何这种邪恶.我认为真正的答案是,你应该永远不要依赖于暴露的接口/合同以外的任何其他类型的返回类型等.做任何其他违反封装,阻止你交换其他人没有的实现使用List而不只是T []等等.
编辑:
像你提到的那样,基本上违反Liskov Substition Principle,以获得所有的技术和东西.
| 归档时间: |
|
| 查看次数: |
1680 次 |
| 最近记录: |