通用类型作为通用类型

3

我想在另一个泛型类型中使用通用类型。

但是遇到以下异常:

类型'MockManagers.ToPos'不能用作泛型类型或方法'MockManagers.IMockManager<Req,Res>'中的类型参数'Res'。 从'MockManagers.ToPos'到'MockManagers.MockResponseObject<System.IComparable>'没有隐式引用转换。

我尝试了一些选项来解决这个问题,但都没有成功。 这个参考文献建议将内部通用类型作为参数传递给更高级别的通用类型。这是唯一的解决方案吗?

代码:
我的接口:

/// <summary>
/// Represents IMockManager for mock object creation object.
/// </summary>
/// <typeparam name="Req">The request object Type.</typeparam>
/// <typeparam name="Res">The response object type.</typeparam>
public interface IMockManager<Req, Res>
    where Req : MockRequestObject
    where Res : MockResponseObject<IComparable>
{...//interface definitions}

对象实现:

/// <summary>
/// Represents Mock response for a request object
/// </summary>
public class ToPos : MockResponseObject<byte[]>
{... //implementation}

public class MockBaseObject
{
    public int Id { get; set; }
}

 public class MockResponseObject<T> : MockBaseObject
{
    /// <summary>
    /// The response content.
    /// </summary>
    public T Content { get; set; }

    /// <summary>
    /// Res creation date.
    /// </summary>
    public DateTime CreationDate { get; set; }
}

1
byte[] 一定是 IComparable 吗?我认为它们的数组不应该是。如果你移除 Res 的限制,它能工作吗? - Rup
使用相同的方法尝试删除byte[]并使用byte时遇到了相同的问题。 - Roi Shabtai
请查看编辑历史 - 我已经标记了引用区域中的代码,以便它实际上有意义(因为像<Generic>这样的标记会变得隐藏)。我不会再做一遍,但请您自己完成。 - Damien_The_Unbeliever
@Damien_The_Unbeliever:没问题。很抱歉那个。 - Roi Shabtai
eee...哪个类继承了IMockManager接口?_roolyeye_接口是在哪里实现的? - Alex F
@Jasper,你无需定义一个类就可以重现这个问题:只需声明一个带有接口类型的变量,例如IMockManager<MockRequestObject, ToPos> foo = null; - Rup
2个回答

3
问题在于你想要对一个类进行泛型变量。但是C#只支持对接口进行泛型变量。
以下是你所期望的简化版本:
// Works fine: "string" is compatible with "IComparable"
IComparable a = new string('a', 5);

// Error: "List<string>" is not compatible with List<IComparable>"
List<IComparable> b = new List<string>(); 

这只适用于接口,并且仅当它们满足某些变量约束时才能实现。其中一个这样的接口是 IEnumerable<out T> 接口:

// Works fine: IEnumerable is covariant
IEnumerable<IComparable> c = new List<string>();

// Similarly, IEnumerable<string> is compatible with IEnumerable<IComparable>:
IEnumerable<string> d = null;
IEnumerable<IComparable> e = d;

那么,你该如何解决这个问题呢? 这里有一个想法。

首先,由于它不是IComparable,所以不能使用byte[]。 让我们以string为例,但你需要找到另一个适合实现此接口的内容。

第二,将MockResponseObject定义为一个接口。此外,将其在T协变

public interface IMockResponseObject<out T>
{
    T Content { get; }
    DateTime CreationDate { get; set; }
}

为了使这个工作正常,内容不能通过接口进行设置。
最后,更新剩余的代码以使用此接口:
interface IMockManager<Req, Res>
    where Req : MockRequestObject
    where Res : IMockResponseObject<IComparable>
{
}

public class ToPos : MockBaseObject, IMockResponseObject<string>
{
    public string Content
    {
        get { throw new NotImplementedException(); }
        set { throw new NotImplementedException(); }
    }

    public DateTime CreationDate
    {
        get { throw new NotImplementedException(); }
        set { throw new NotImplementedException(); }
    }
}

请注意,ToPos 仍然有一个具有 setter 的 Content 属性:只要不通过接口设置内容,您就可以设置内容。
经过所有这些更改,以下代码现在是有效的并成功编译:
static IMockManager<MockRequestObject, ToPos> manager = null;

2

你可以将ToPos声明为从MockResponseObject<IComparable>派生而来,而不是MockResponseObject<byte[]>,但是你需要用一个类型为byte[]的新属性隐藏Content属性。然而,为了使其正常工作,你需要用一个包装byte[]并实现IComparable的东西。例如:

public class ByteArrayComparable : IComparable
{
    public byte[] Data { get; private set; }
    public ByteArrayComparable(byte[] data) { Data = data; }

    // Implement IComparable.CompareTo() method here!
    public int CompareTo(object obj) { ... }
}

public class ToPos : MockResponseObject<IComparable>
{
    public new byte[] Content
    {
        get
        {
            if (base.Content == null)
                return null;
            return ((ByteArrayComparable) base.Content).Data;
        }
        set
        {
            base.Content = value == null ? null : new ByteArrayComparable(value);
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接