我有一个对象的继承结构,有点像:
public class A { }
public class B : A { }
public class C : B { }
理想情况下,我希望能够将List
A
,B
或C
传递给这样的单一方法:
private void Method(List<A> foos) { /* Method Implementation */ }
B toBee = new B();
B notToBee = new B();
List<B> hive = new List<B> { toBee, notToBee };
// Call Method() with a inherited type. This throws a COMPILER ERROR
// because although B is an A, a List<B> is NOT a List<A>.
Method(hive);
我想想出一种方法来获得相同的功能,尽可能少的代码重复。
我能想到的最好的方法是创建包含各种类型列表的包装器方法,然后遍历传递的列表来调用相同的方法;最后使用多态对我有利:
private void Method(List<A> foos) { foreach (var foo in foos) Bar(foo); }
private void Method(List<B> foos) { foreach (var foo in foos) Bar(foo); }
private void Method(List<C> foos) { foreach (var foo in foos) Bar(foo); }
// An A, B or C object can be passed to this method thanks to polymorphism
private void Bar(A ayy) { /* Method Implementation */ }
总线如您所见,我已经三次复制并粘贴该方法,只更改列表通用中包含的类型。我开始相信,无论何时开始复制和粘贴代码,都有更好的方法来做到这一点......但我似乎无法想出一个。
如果没有不受欢迎的复制和粘贴,我怎么能完成这样的壮举?
答案 0 :(得分:11)
private void Method<T>(List<T> foos)
因此您可以将其用于各种List<T>
。您还可以使用generic constraints缩小方法的接受参数列表,以便仅处理A
子类:
private void Method<T>(List<T> foos)
where T : A
然后您确定foos
的每个元素都可以用作A
的实例:
private void Method<T>(List<T> foos)
where T : A
{
foreach (var foo in foos)
{
var fooA = foo as A;
// fooA != null always (if foo wasn't null already)
Bar(fooA);
}
}
正如Lucas Trzesniewski在他的回答中指出的那样,它甚至更好
如果您不需要修改集合,请使用IEnumerable<T>
。这是协变的,所以你不会遇到你所描述的问题。
答案 1 :(得分:8)
您需要covariance。
如果您可以写下以下内容:
List<T>
然后它首先仅表示 IEnumerable<out T>
是错误的参数类型。
最好更好地使用IEnumerable<C>
,原因有两个:
IEnumerable<A>
是HashSet<B>
LinkedList<A>
,C[]
或private void Method(IEnumerable<A> foos)
{
foreach (var foo in foos)
Whatever(foo);
}
,它仍可以正常使用。List<T>
T
是机制 - 它将IEnumerable<out T>
引用(或值类型的值)存储在数组的连续内存中。您不要求此处的特定财产。
相比之下,IReadOnlyCollection<out T>
是合同。它只表示你可以枚举一个序列,在这种情况下你只需要它。
您可以使用其他协变接口类型,例如IReadOnlyList<out T>
(如果您需要预先了解项目计数或必须多次枚举)或IList<T>
(如果您需要项目)索引)。
请记住,大多数情况下,尽可能将接口类型作为参数,因为它允许您在需要时切换底层实现。
但如果你真的需要修改列表,那么协方差就不够了。在这种情况下,请使用BartoszKP的解决方案,但是,嘿,您仍然可以使用List<T>
代替 Private Sub Enable_Proxy(ByVal Proxyset As String)
Dim regkeyProxy_Enable As RegistryKey
'Registry Key Location
regkeyProxy_Enable = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", True)
'Change The Proxy Enable value
regkeyProxy_Enable.SetValue("ProxyEnable", 1, RegistryValueKind.DWord)
'Set the proxy server value
regkeyProxy_Enable.SetValue("ProxyServer", Proxyset, RegistryValueKind.String)
End Sub
Private Sub Disable_Proxy()
Dim regkeyProxy_Enable As RegistryKey
regkeyProxy_Enable = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", True)
'Disable The proxy
regkeyProxy_Enable.SetValue("ProxyEnable", 0, RegistryValueKind.DWord)
End Sub
: - )