C#泛型协变

C#泛型协变,c#,generics,C#,Generics,我遇到了一个关于泛型的问题,我相信它可以用协方差来解决,但我并不完全理解协方差是如何工作的,以及如何正确地声明它。 假设我有以下接口和类: public interface IOwnedObject<TUser> where TUser : IBaseUser { string UserId { get; set; } TUser User { get; set; } } public interface IBaseUser { string Id { ge

我遇到了一个关于泛型的问题,我相信它可以用协方差来解决,但我并不完全理解协方差是如何工作的,以及如何正确地声明它。 假设我有以下接口和类:

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; }
}

既然
User
实现了
ibasueser
,那么
IOwnedObject
在技术上不应该是
IOwnedObject
的基类。是否可以在不引用具体实现的情况下将第一条语句求值为true?

只需对界面进行一些修改,就可以将第一条语句求值为true。我们希望将
TUser
类型标记为与
out
关键字协变,在这样做时,我们必须删除属性setter

public interface IOwnedObject<out TUser> where TUser : IBaseUser
{
    string UserId { get; set; }
    TUser User { get; }
}
公共接口IOwnedObject,其中TUser:IBaseUser
{
字符串用户标识{get;set;}
t用户{get;}
}
现在评估

var obj = new SomeOwnedObject();
if (obj is IOwnedObject<IBaseUser> o)
    Console.WriteLine("Success");
var obj=newsomeownedObject();
如果(obj是IOwnedObject o)
Console.WriteLine(“成功”);

将导致“成功”被打印到控制台。

执行@Jonathon的建议或以下操作。要使其协变,您需要使用不变类型,即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; }
}
publicstaticvoidmain()
{
var obj=新的SomeOwnedObject();
如果(obj是IOwnedObject o)
Console.WriteLine(“Success”);//这永远不会执行
}
公共接口IOwnedObject,其中TUser:IBaseUser
{
字符串用户标识{get;set;}
IBaseUser用户{get;set;}
}
公共接口IBaseUser
{
字符串Id{get;set;}
}
公共类用户:IBaseUser
{
公共字符串Id{get;set;}
}
公共类SomeOwnedObject:IOownedObject
{
公共字符串用户标识{get;set;}
公共IBaseUser用户{get;set;}
}

c#没有模板。它有泛型。“从技术上讲,IOwnedObject不应该是IOwnedObject的基类吗”-只是确保您已经看到-(或任何其他)感谢您提到删除属性设置器。在类型参数上添加out或in keyworks时,我无法理解错误的含义。很好@Brad Yep,
get
仅用于协变,
set
仅用于逆变(
in
),两者均用于不变。
out
in
中的关键字标记通用差异,并描述如何使用类型。在中标记为
的泛型参数可以将
中的
类型用作方法参数的类型,但不能用作方法的返回类型。标记为
out
的泛型参数可以将该类型用作方法的返回类型,但不能用作方法的参数之一。由于getter和setter只是将要生成的方法的糖分,因此规则也适用于它们。这将完全删除泛型类型参数,因为没有使用该参数。它最终成为声明的约束,但不会像预期的那样将属性类型限制为提供的类型。
var obj = new SomeOwnedObject();
if (obj is IOwnedObject<IBaseUser> o)
    Console.WriteLine("Success");
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; }
}