- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
首先,我阅读了很多关于 SO 的解释以及关于协变和逆变的博客,非常感谢 Eric Lippert在 Covariance and Contravariance 上制作了如此出色的系列.
不过,我有一个更具体的问题,我想稍微了解一下。
据我了解 Eric's explanation是协变和逆变都是描述转换的形容词。协变变换是保留类型顺序的变换,而逆变变换是反转它的变换。
我认为大多数开发人员都可以凭直觉理解协方差。
//covariant operation
Animal someAnimal = new Giraffe();
//assume returns Mammal, also covariant operation
someAnimal = Mammal.GetSomeMammal();
这里的返回操作是协变的,因为我们保留了 Animal 仍然大于 Mammal 或 Giraffe 的大小。关于这一点,大多数返回操作都是协变的,逆变操作没有意义。
//if return operations were contravariant
//the following would be illegal
//as Mammal would need to be stored in something
//equal to or less derived than Mammal
//which would mean that Animal is now less than or equal than Mammal
//therefore reversing the relationship
Animal someAnimal = Mammal.GetSomeMammal();
这段代码当然对大多数开发人员来说没有意义。
我的困惑在于逆变参数参数。如果你有这样的方法
bool Compare(Mammal mammal1, Mammal mammal2);
我一直了解到输入参数总是强制逆变行为。这样,如果该类型用作输入参数,则其行为应该是逆变的。
但是下面的代码有什么区别
Mammal mammal1 = new Giraffe(); //covariant
Mammal mammal2 = new Dolphin(); //covariant
Compare(mammal1, mammal2); //covariant or contravariant?
//or
Compare(new Giraffe(), new Dolphin()); //covariant or contravariant?
同样的道理,你不能做这样你不能做的事
//not valid
Mammal mammal1 = new Animal();
//not valid
Compare(new Animal(), new Dolphin());
我想我想问的是,是什么让方法参数通过逆变转换。
抱歉,这篇文章很长,可能是我理解有误。
编辑:
根据下面的一些对话,我了解到例如使用委托(delegate)层可以清楚地显示逆变。考虑以下示例
//legal, covariance
Mammal someMammal = new Mammal();
Animal someAnimal = someMammal;
// legal in C# 4.0, covariance (because defined in Interface)
IEnumerable<Mammal> mammalList = Enumerable.Empty<Mammal>();
IEnumerable<Animal> animalList = mammalList;
//because of this, one would assume
//that the following line is legal as well
void ProcessMammal(Mammal someMammal);
Action<Mammal> processMethod = ProcessMammal;
Action<Animal> someAction = processMethod;
当然这是非法的,因为有人可以将任何 Animal 传递给 someAction,而 ProcessMammal 期望任何 Mammal 或更具体(小于 Mammal)的东西。这就是为什么 someAction 只能是 Action 或任何更具体的 (Action)
然而,这在中间引入了一层委托(delegate),是否必须在中间有一个委托(delegate)才能发生逆变投影?如果我们将 Process 定义为接口(interface),我们会将参数参数声明为逆变类型只是因为我们不希望有人能够使用委托(delegate)执行我上面显示的操作?
public interface IProcess<out T>
{
void Process(T val);
}
最佳答案
更新:糟糕。事实证明,我在最初的回答中混淆了方差和“分配兼容性”。相应地编辑了答案。我还写了一篇博文,希望能更好地回答这些问题:Covariance and Contravariance FAQ
答案:我猜你第一个问题的答案是你在这个例子中没有逆变:
bool Compare(Mammal mammal1, Mammal mammal2);
Mammal mammal1 = new Giraffe(); //covariant - no
Mammal mammal2 = new Dolphin(); //covariant - no
Compare(mammal1, mammal2); //covariant or contravariant? - neither
//or
Compare(new Giraffe(), new Dolphin()); //covariant or contravariant? - neither
此外,这里甚至没有协方差。您所拥有的称为“分配兼容性”,这意味着您始终可以将派生程度更高的类型的实例分配给派生程度更低的类型的实例。
在 C# 中,数组、委托(delegate)和通用接口(interface)支持变体。正如 Eric Lippert 在他的博客文章中所说 What's the difference between covariance and assignment compatibility?是最好将方差视为类型的“投影”。
协变更容易理解,因为它遵循赋值兼容性规则(派生类型多的数组可以赋值给派生类型少的数组,“object[] objs = new string[10];”)。逆变颠倒了这些规则。例如,假设您可以执行类似“string[] strings = new object[10];”的操作。当然,由于显而易见的原因,您不能这样做。但那将是逆变(但同样,数组不是逆变的,它们仅支持协变)。
以下是来自 MSDN 的示例,我希望它们能向您展示逆变的真正含义(我现在拥有这些文档,所以如果您认为文档中有什么不清楚的地方,请随时给我反馈):
Using Variance in Interfaces for Generic Collections
Employee[] employees = new Employee[3];
// You can pass PersonComparer,
// which implements IEqualityComparer<Person>,
// although the method expects IEqualityComparer<Employee>.
IEnumerable<Employee> noduplicates =
employees.Distinct<Employee>(new PersonComparer());
// Event hander that accepts a parameter of the EventArgs type.
private void MultiHandler(object sender, System.EventArgs e)
{
label1.Text = System.DateTime.Now.ToString();
}
public Form1()
{
InitializeComponent();
// You can use a method that has an EventArgs parameter,
// although the event expects the KeyEventArgs parameter.
this.button1.KeyDown += this.MultiHandler;
// You can use the same method
// for an event that expects the MouseEventArgs parameter.
this.button1.MouseClick += this.MultiHandler;
}
Using Variance for Func and Action Generic Delegates
static void AddToContacts(Person person)
{
// This method adds a Person object
// to a contact list.
}
// The Action delegate expects
// a method that has an Employee parameter,
// but you can assign it a method that has a Person parameter
// because Employee derives from Person.
Action<Employee> addEmployeeToContacts = AddToContacts;
希望这对您有所帮助。
关于c# - 逆变解释,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1962629/
#include using namespace std; class C{ private: int value; public: C(){ value = 0;
这个问题已经有答案了: What is the difference between char a[] = ?string?; and char *p = ?string?;? (8 个回答) 已关闭
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 7 年前。 此帖子已于 8 个月
除了调试之外,是否有任何针对 c、c++ 或 c# 的测试工具,其工作原理类似于将独立函数复制粘贴到某个文本框,然后在其他文本框中输入参数? 最佳答案 也许您会考虑单元测试。我推荐你谷歌测试和谷歌模拟
我想在第二台显示器中移动一个窗口 (HWND)。问题是我尝试了很多方法,例如将分辨率加倍或输入负值,但它永远无法将窗口放在我的第二台显示器上。 关于如何在 C/C++/c# 中执行此操作的任何线索 最
我正在寻找 C/C++/C## 中不同类型 DES 的现有实现。我的运行平台是Windows XP/Vista/7。 我正在尝试编写一个 C# 程序,它将使用 DES 算法进行加密和解密。我需要一些实
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
有没有办法强制将另一个 窗口置于顶部? 不是应用程序的窗口,而是另一个已经在系统上运行的窗口。 (Windows, C/C++/C#) 最佳答案 SetWindowPos(that_window_ha
假设您可以在 C/C++ 或 Csharp 之间做出选择,并且您打算在 Windows 和 Linux 服务器上运行同一服务器的多个实例,那么构建套接字服务器应用程序的最明智选择是什么? 最佳答案 如
你们能告诉我它们之间的区别吗? 顺便问一下,有什么叫C++库或C库的吗? 最佳答案 C++ 标准库 和 C 标准库 是 C++ 和 C 标准定义的库,提供给 C++ 和 C 程序使用。那是那些词的共同
下面的测试代码,我将输出信息放在注释中。我使用的是 gcc 4.8.5 和 Centos 7.2。 #include #include class C { public:
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我的客户将使用名为 annoucement 的结构/类与客户通信。我想我会用 C++ 编写服务器。会有很多不同的类继承annoucement。我的问题是通过网络将这些类发送给客户端 我想也许我应该使用
我在 C# 中有以下函数: public Matrix ConcatDescriptors(IList> descriptors) { int cols = descriptors[0].Co
我有一个项目要编写一个函数来对某些数据执行某些操作。我可以用 C/C++ 编写代码,但我不想与雇主共享该函数的代码。相反,我只想让他有权在他自己的代码中调用该函数。是否可以?我想到了这两种方法 - 在
我使用的是编写糟糕的第 3 方 (C/C++) Api。我从托管代码(C++/CLI)中使用它。有时会出现“访问冲突错误”。这使整个应用程序崩溃。我知道我无法处理这些错误[如果指针访问非法内存位置等,
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,因为
我有一些 C 代码,将使用 P/Invoke 从 C# 调用。我正在尝试为这个 C 函数定义一个 C# 等效项。 SomeData* DoSomething(); struct SomeData {
这个问题已经有答案了: Why are these constructs using pre and post-increment undefined behavior? (14 个回答) 已关闭 6
我是一名优秀的程序员,十分优秀!