我正在尝试施放逆变委托但由于某种原因我只能使用"as"运算符.
interface MyInterface { }
delegate void MyFuncType<in InType>(InType input);
class MyClass<T> where T : MyInterface
{
public void callDelegate(MyFuncType<MyInterface> func)
{
MyFuncType<T> castFunc1 = (MyFuncType <T>) func; //Error
MyFuncType<T> castFunc2 = func as MyFuncType<T>;
MyFuncType<T> castFunc3 = func is MyFuncType<T> ? (MyFuncType<T>)func : (MyFuncType<T>)null; //Error
}
}
Run Code Online (Sandbox Code Playgroud)
castFunc2工作正常,但castFunc1和castFunc3导致错误:
Cannot convert type 'delegateCovariance.MyFuncType<myNamespace.MyInterface>' to myNamespace.MyFuncType<T>'
Run Code Online (Sandbox Code Playgroud)
在上为运营商MSDN文章指出,castFunc2和castFunc3"等价于"所以我不明白怎么只有一个可能会导致错误.另一部令我困惑的是将MyInterface从接口更改为类可以消除错误.
任何人都可以帮我理解这里发生了什么?谢谢!
在Java中,协方差允许API设计者指定实例可以被概括为某种类型或任何该类型的子类型.例如:
List<? extends Shape> shapes = new ArrayList<Circle>();
// where type Circle extends Shape
Run Code Online (Sandbox Code Playgroud)
反方差则是另一种方式.它允许我们指定实例可以概括为某种类型或超类型.
List<? super Shape> shapes = new ArrayList<Geometry>();
// where Shape extends Geometry
Run Code Online (Sandbox Code Playgroud)
Java泛型的逆变是如何有用的?你什么时候选择使用它?
Resharper建议改变
interface IModelMapper<TFrom, TTo>
{
TTo Map(TFrom input);
}
Run Code Online (Sandbox Code Playgroud)
成
interface IModelMapper<in TFrom, out TTo>
Run Code Online (Sandbox Code Playgroud)
所以我调查了一下,结束了阅读这篇文章(通过维基百科的文章找到)和更多的谷歌.
我仍然不确定这对我的申请意味着什么,所以我很想接受这个建议.这种改变会带来什么好处,我不考虑忽视这个建议?
更明确的是,我为什么要接受它?
假设我有接口和类:
public interface ITree {}
public class Tree : ITree {}
Run Code Online (Sandbox Code Playgroud)
由于IEnumerable<T>是协变,下面的代码行成功编译:
IEnumerable<ITree> trees = new List<Tree>();
Run Code Online (Sandbox Code Playgroud)
但是当我把它放入通用方法时:
public void Do<T>() where T : ITree
{
IEnumerable<ITree> trees = new List<T>();
}
Run Code Online (Sandbox Code Playgroud)
我从编译器得到编译错误:
错误1无法将类型'System.Collections.Generic.List'隐式转换为'System.Collections.Generic.IEnumerable'.存在显式转换(您是否缺少演员?)D:\ lab\Lab.General\Lab.General\Program.cs 83 40 Lab.General
为什么协方差在这种情况下不起作用?
在.NET中了解有关标准事件模型的更多信息时,我发现在引入C#中的泛型之前,处理事件的方法由此委托类型表示:
//
// Summary:
// Represents the method that will handle an event that has no event data.
//
// Parameters:
// sender:
// The source of the event.
//
// e:
// An object that contains no event data.
public delegate void EventHandler(object sender, EventArgs e);
Run Code Online (Sandbox Code Playgroud)
但是在C#2中引入泛型之后,我认为这个委托类型是使用泛型重写的:
//
// Summary:
// Represents the method that will handle an event when the event provides data.
//
// Parameters:
// sender:
// The source of the event.
//
// e:
// …Run Code Online (Sandbox Code Playgroud) 这种限制的真正原因是什么?这只是必须完成的工作吗?概念上难吗?这不可能吗?
当然,人们不能在字段中使用类型参数,因为它们总是读写.但这不是答案,可以吗?
这个问题的原因是我在C#4上写了一篇关于方差支持的文章,我觉得我应该解释为什么它仅限于委托和接口.只是为了逆转举证责任.
更新: 埃里克问了一个例子.
怎么样(不知道这是否有意义,但是:-))
public class Lookup<out T> where T : Animal {
public T Find(string name) {
Animal a = _cache.FindAnimalByName(name);
return a as T;
}
}
var findReptiles = new Lookup<Reptile>();
Lookup<Animal> findAnimals = findReptiles;
Run Code Online (Sandbox Code Playgroud)
在一个类中拥有它的原因可能是类本身中保存的缓存.请不要将您的不同类型的宠物命名为相同!
顺便说一句,这让我想到了C#5.0中的可选类型参数 :-)
更新2:我没有声称CLR和C#应该允许这个.只是想了解是什么原因导致它没有.
有IsAssignableFrom方法返回一个布尔值,表示一种类型是否可以从另一种类型分配.
怎能不只有他们分配的测试从或到对方,但也知道了最低协变,以获得最佳类型?
考虑以下示例(C#4.0)
码
// method body of Func is irrelevant, use default() instead
Func<char[]> x = default(Func<char[]>);
Func<int[]> y = default(Func<int[]>);
Func<Array> f = default(Func<Array>);
Func<IList> g = default(Func<IList>);
g=x;
g=y;
y=x; // won't compile
x=y; // won't compile
// following two are okay; Array is the type for the covariance
f=x; // Array > char[] -> Func<Array> > Func<char[]>
f=y; // Array > int[] -> Func<Array> > Func<int[]>
// following …Run Code Online (Sandbox Code Playgroud)我有一个内部可变性的结构.
use std::cell::RefCell;
struct MutableInterior {
hide_me: i32,
vec: Vec<i32>,
}
struct Foo {
//although not used in this particular snippet,
//the motivating problem uses interior mutability
//via RefCell.
interior: RefCell<MutableInterior>,
}
impl Foo {
pub fn get_items(&self) -> &Vec<i32> {
&self.interior.borrow().vec
}
}
fn main() {
let f = Foo {
interior: RefCell::new(MutableInterior {
vec: Vec::new(),
hide_me: 2,
}),
};
let borrowed_f = &f;
let items = borrowed_f.get_items();
}
Run Code Online (Sandbox Code Playgroud)
产生错误:
error[E0597]: borrowed value does not live long enough
--> …Run Code Online (Sandbox Code Playgroud) encapsulation contravariance mutability rust interior-mutability
interface ICloneable<out T>
{
T Clone();
}
class Base : ICloneable<Base>
{
public Base Clone() { return new Base(); }
}
class Derived : Base, ICloneable<Derived>
{
new public Derived Clone() { return new Derived(); }
}
Run Code Online (Sandbox Code Playgroud)
鉴于这些类型声明,C#规范的哪一部分解释了为什么以下代码片段的最后一行打印"True"?开发人员可以依赖这种行为吗?
Derived d = new Derived();
Base b = d;
ICloneable<Base> cb = d;
Console.WriteLine(b.Clone() is Derived); // "False": Base.Clone() is called
Console.WriteLine(cb.Clone() is Derived); // "True": Derived.Clone() is called
Run Code Online (Sandbox Code Playgroud)
需要注意的是,如果T在类型参数ICloneable都没有声明out,则这两条线将打印"假".
回顾Java 8 StreamAPI设计,我对Stream.reduce()参数的泛型不变性感到惊讶:
<U> U reduce(U identity,
BiFunction<U,? super T,U> accumulator,
BinaryOperator<U> combiner)
Run Code Online (Sandbox Code Playgroud)
相同API的看似更通用的版本可能在个别引用上应用了协方差/逆变U,例如:
<U> U reduce(U identity,
BiFunction<? super U, ? super T, ? extends U> accumulator,
BiFunction<? super U, ? super U, ? extends U> combiner)
Run Code Online (Sandbox Code Playgroud)
这将允许以下目前无法实现的目标:
// Assuming we want to reuse these tools all over the place:
BiFunction<Number, Number, Double> numberAdder =
(t, u) -> t.doubleValue() + u.doubleValue();
// This currently doesn't work, but would work with the suggestion
Stream<Number> stream …Run Code Online (Sandbox Code Playgroud) contravariance ×10
c# ×7
covariance ×6
generics ×4
c#-4.0 ×3
java ×2
.net ×1
.net-4.0 ×1
.net-core ×1
casting ×1
invariance ×1
java-stream ×1
mutability ×1
rust ×1
types ×1
variance ×1