今天,我在Java中阅读了一些关于Covariance,Contravariance(和Invariance)的文章.我阅读了英文和德文维基百科的文章,以及IBM的一些其他博客文章和文章.
但我对这些究竟是什么有点困惑?有人说,它是关于类型和子类型之间的关系,也有人说,它是关于类型转换和一些说,它是用来决定一个方法是否重载或超载.
所以我正在用简单的英语寻找一个简单的解释,它向初学者展示了Covariance和Contravariance(和Invariance).加上一点简单的例子.
我创建了以下函数:
public void DelegatedCall(Action<Object> delegatedMethod)
Run Code Online (Sandbox Code Playgroud)
并定义了以下方法
public void foo1(String str) { }
Run Code Online (Sandbox Code Playgroud)
但是,当我尝试打电话DelegateCall时foo1:
DelegatedCall(foo1);
Run Code Online (Sandbox Code Playgroud)
...我收到以下编译器错误:
Argument 1: cannot convert from 'method group' to 'System.Action<object>'
这个错误的原因是什么,我该如何纠正?不幸的是,铸造foo1到Action是不是一种选择.
下面的代码是实现协变返回类型的唯一方法吗?
public abstract class BaseApplication<T> {
public T Employee{ get; set; }
}
public class Application : BaseApplication<ExistingEmployee> {}
public class NewApplication : BaseApplication<NewEmployee> {}
Run Code Online (Sandbox Code Playgroud)
我希望能够构造一个Application或NewApplication并让它从Employee属性返回适当的Employee类型.
var app = new Application();
var employee = app.Employee; // this should be of type ExistingEmployee
Run Code Online (Sandbox Code Playgroud)
我相信这段代码工作得很好,但是当我有几个需要相同行为的属性时,它变得非常讨厌.
有没有其他方法来实现这种行为?泛型或其他?
从书中:
1) int i = 7;
2) Object o = i; // Implicit boxing int-->Object
3) Object[] a3 = new int[] { 1, 2 }; // Illegal: no array conversion
Run Code Online (Sandbox Code Playgroud)
3)中的赋值是非法的,因为int不是引用类型,因此int []不能隐式转换为Object []
我不懂.在第2行)它显示int可以隐式转换为Object,在第三行中,它表示int []不可隐式转换.哇?
这就是我想要的
(IList<Foo>)listPropertyInfo.GetValue(item)
Run Code Online (Sandbox Code Playgroud)
这就是我获得Foo类型的方式
listPropertyInfo.GetValue(item).GetType().GenericTypeArguments[0]
Run Code Online (Sandbox Code Playgroud)
这是我尝试但无法成功的原因
Convert.ChangeType(listPropertyInfo.GetValue(item), IList<listPropertyInfo.GetValue(item).GetType().GenericTypeArguments[0]>)
Run Code Online (Sandbox Code Playgroud)
还有这个;
((typeof(IList<>).MakeGenericType(listPropertyInfo.GetValue(item).GetType().GenericTypeArguments.Single())))(listPropertyInfo.GetValue(item))
Run Code Online (Sandbox Code Playgroud)
这是我试图实现的方法
public static void trigger(IList<T> result)
{
foreach (var item in result)
{
foreach (var listPropertyInfo in typeof(T).GetProperties().ToList().FindAll(x => x.PropertyType.Name == typeof(IList<>).Name))
{
trigger((IList<Foo>)listPropertyInfo.GetValue(item));
}
}
}
Run Code Online (Sandbox Code Playgroud) 一段C#代码
var isTrue = (new List<int>{1,2,3} is IEnumerable<object>);
Run Code Online (Sandbox Code Playgroud)
我得到false了代码执行的结果,但是当我将该代码复制到WATCH窗口时,结果是true.
我试图在C#中的泛型类型参数中使用多态.我已经在SO(审查其他几个问题1,2,3,4,5,6),但我仍然不清楚为什么这不工作(或者如果它甚至允许的).
我有以下课程:
public class Base { }
public class Derived<T> : Base { }
public class Foo { }
Run Code Online (Sandbox Code Playgroud)
和我的派生泛型类型的实例:
var derived = new Derived<Foo>();
Run Code Online (Sandbox Code Playgroud)
以下陈述都是正确的:
derived is Object
derived is Base
derived is Derived<Foo>
Run Code Online (Sandbox Code Playgroud)
当我尝试将我的派生类用作另一个泛型类型中的类型参数时,我得到一些意外的行为.鉴于以下惰性实例:
var lazy = new Lazy<Derived<Foo>>();
Run Code Online (Sandbox Code Playgroud)
以下是真实的:
lazy is Lazy<Derived<Foo>>
Run Code Online (Sandbox Code Playgroud)
但是当我预期它们是真的时,这些都是错误的:
lazy is Lazy<Object>
lazy is Lazy<Base>
Run Code Online (Sandbox Code Playgroud)
为什么是这样?它们应该是真的还是我误解了仿制药是如何工作的?
我有一个需要协变,因此,在另一个类*的覆盖可以返回一个类型的住房Shelter<Cat>,其中一个Shelter<Animal>预期.由于类在C#中不能是共变或逆变,我添加了一个接口:
public interface IShelter<out AnimalType>
{
AnimalType Contents { get; }
}
Run Code Online (Sandbox Code Playgroud)
然而,有一个地方,IShelter(编译时类型)被分配了一个新的动物,我们确定所设置的动物将成为一只猫.起初,我以为我可以在Contents属性中添加一个集合并执行:
IShelter<Cat> shelter = new Shelter(new Cat());
shelter.Contents = new Cat();
Run Code Online (Sandbox Code Playgroud)
但是添加setter是不可能的;
Error CS1961 Invalid variance: The type parameter 'AnimalType' must be invariantly valid on 'IShelter<AnimalType>.Contents'. 'AnimalType' is covariant.
Run Code Online (Sandbox Code Playgroud)
这是有道理的,因为否则我可以将cathelter传递给这个函数:
private static void UseShelter(IShelter<Animal> s)
{
s.Contents = new Lion();
}
Run Code Online (Sandbox Code Playgroud)
但是,我不打算这样做.有一些方法可以将setter标记为不变量,这样UseShelter函数只能分配一个Animal,这样就可以在编译时强制执行.我需要这个的原因是因为我的代码中有一个地方知道它有一个Shelter<Cat>,并且需要将Contents属性重新分配给一个新的Cat.
到目前为止我找到的解决方法是在显式set函数中添加一个jucky运行时类型检查; Juck!
public void SetContents(object newContents)
{
if (newContents.GetType() != typeof(AnimalType))
{
throw new InvalidOperationException("SetContents must be given the correct …Run Code Online (Sandbox Code Playgroud)
- 委托可以具有比其方法目标更具体的参数类型.这称为逆变
- 委托的返回类型可以比其目标方法的返回类型更不具体.这称为协方差
而且,这是一个例子.
using System;
delegate void StringAction(string s);
delegate object ObjectRetriever();
class Test
{
static void Main()
{
StringAction sa = new StringAction(ActionObject);
sa("hello");
ObjectRetriever o = new ObjectRetriever(RetrieveString);
object result = o();
Console.WriteLine(result);
}
static string RetrieveString() {return "hello";}
static void ActionObject(object o)
{
Console.WriteLine(o);
}
}
Run Code Online (Sandbox Code Playgroud)
我认为为了使用协方差/逆变,需要使用new如示例中所示,但我似乎得到了相同的结果sa = ActionObject和 o = RetrieveString.(我用Mono测试过).
new用来解释协方差/逆变?object x = Everything inherit from object?这个奇怪的名字来自哪里?它的用途是什么?这可能是一个非常简单的问题,但对我来说没有意义.
鉴于此类:
public class Person : ICloneable {
public object Clone()
{
Console.WriteLine("Hello, world");
return new Person();
}
}
Run Code Online (Sandbox Code Playgroud)
为什么这样可以?
List<Person> people = new List<Person> { new Person() };
IEnumerable<ICloneable> clonables = people;
Run Code Online (Sandbox Code Playgroud)
但这不是吗?
List<Person> people = new List<Person> { new Person() };
IList<ICloneable> clonables = people;
Run Code Online (Sandbox Code Playgroud)
为什么我可以分配给IEnumerable IClonable,而不是IList ICloneable?
c# ×9
covariance ×4
generics ×4
.net ×2
casting ×1
debugging ×1
delegates ×1
java ×1
method-group ×1
polymorphism ×1