简体   繁体   English

C#泛型协变

[英]C# Generics covariant

I ran into an issue with generics which I believed can be solved with covariance, but don't fully understand how covarience works and how it can be declared properly. 我遇到了一个泛型问题,我认为可以通过协方差解决,但我不完全了解协方差的工作原理以及如何正确声明它。 Let's say I have the following interfaces and classes: 假设我有以下接口和类:

public interface IOwnedObject<TUser>
where TUser : IBaseUser
{
    string UserId { get; set; }
    TUser User { get; set; }
}

public interface IBaseUser
{
    string Id { get; set; }
}

public class User : IBaseUser
{
    public string Id { get; set; }
}

public class SomeOwnedObject : IOwnedObject<User>
{
    public string UserId { get; set; }
    public User User { get; set; }
}

Then consider the following code: 然后考虑以下代码:

var obj = new SomeOwnedObject();
            if(obj is IOwnedObject<IBaseUser> o)
                Console.WriteLine("Success"); // This never executes

The above if statement does not evaluate to true. 上面的if语句的计算结果不为true。 However, in IOwnedObject it's only ever possible for TUser to be IBaseUser . 但是,在IOwnedObject ,只有TUser才能成为IBaseUser

The following code evaluates to true: 以下代码评估为true:

var obj = new SomeOwnedObject();
            if(obj is IOwnedObject<User> o)
                Console.WriteLine("Success"); // This executes

since User implements IBaseUser , shouldn't IOwnedObject<IBaseUser> technically be a base class of IOwnedObject<User> . 由于User实现了IBaseUser ,因此IOwnedObject<IBaseUser>技术IOwnedObject<IBaseUser>不应是IOwnedObject<User>的基类。 Is it possible to make that first statement evaluate to true without referencing the concrete implementation User ? 是否可以在不引用具体实现User情况下使第一条语句评估为true?

You can get the first statement to evaluate to true with a few modifications to your interface. 您可以通过对界面进行一些修改来获得第一个评估结果为true的语句。 We'll want to mark the TUser type as covariant with the out keyword, and in doing so we'll have to remove the property setter. 我们将要使用out关键字将TUser类型标记为协变,这样我们就必须删除属性设置器。

public interface IOwnedObject<out TUser> where TUser : IBaseUser
{
    string UserId { get; set; }
    TUser User { get; }
}

Now evaluating 现在评估

var obj = new SomeOwnedObject();
if (obj is IOwnedObject<IBaseUser> o)
    Console.WriteLine("Success");

will result in "Success" being printed to the console. 将导致“成功”被打印到控制台。

Either do @Jonathon's suggestion or the below. @Jonathon的建议或以下建议。 Making it covariant you need to use an invariant type, which is IBaseUser. 要使其协变,您需要使用不变类型,即IBaseUser。

public static void Main()
{
    var obj = new SomeOwnedObject();
        if(obj is IOwnedObject<IBaseUser> o)
            Console.WriteLine("Success"); // This never executes
}

public interface IOwnedObject<out TUser> where TUser : IBaseUser
{
    string UserId { get; set; }
    IBaseUser User { get; set; }
}

public interface IBaseUser
{
    string Id { get; set; }
}

public class User : IBaseUser
{
    public string Id { get; set; }
}

public class SomeOwnedObject : IOwnedObject<User>
{
    public string UserId { get; set; }
    public IBaseUser User { get; set; }
}

https://dotnetfiddle.net/PAE3CW https://dotnetfiddle.net/PAE3CW

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM