在 SO 上查看 earlier question 时,我开始考虑类将值(例如集合)公开为由值的类型实现的接口(interface)的情况。在下面的代码示例中,我使用了一个列表,并将该列表公开为 IEnumerable。
通过 IEnumerable 接口(interface)公开列表定义了列表仅被枚举而不被修改的意图。但是,由于实例可以重新转换回列表,因此当然可以修改列表本身。
我还在示例中包含了该方法的一个版本,该版本通过在每次调用该方法时将列表项引用复制到新列表来防止修改,从而防止对基础列表进行更改。
所以我的问题是,所有将具体类型公开为已实现接口(interface)的代码都应该通过复制操作来实现吗?语言结构中是否有明确指示“我想通过接口(interface)公开此值,并且调用代码应该只能通过接口(interface)使用此值”的值?当通过他们的接口(interface)公开具体值时,其他人使用什么技术来防止这些意外的副作用。
请注意,我了解所示行为是预期行为。我并不是说这种行为是错误的,只是它确实允许使用表达意图以外的功能。也许我给界面赋予了太多的重要性——将其视为一种功能约束。想法?
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>();
}
}
}
最佳答案
防止这种情况的一种方法是使用 AsReadOnly() 来防止任何此类恶意行为。我认为真正的答案是,在返回类型等方面,除了公开的接口(interface)/契约(Contract)之外,您永远不应该依赖任何其他东西。做任何其他事情都会违反封装,防止您将您的实现换成其他人的实现使用 List 而不是 T[] 等。
编辑:
像你提到的向下转换基本上是对 Liskov Substition Principle 的违反,以获得所有技术和东西。
关于c# - 从接口(interface)转换为底层类型,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/4056013/