繁体   English   中英

如何将派生的 object 添加到从同一接口继承但使用 generics 的对象集合中?

[英]How do I add a derived object to a collection of objects which inherit from the same interface, but use generics?

我对 C# 中的 generics 有点陌生。 我有一个IResult接口,它具有以下属性:

    public interface IResult<T>
    {
         string ResultMessage { get; set; }
         T Data { get; set; }
    }

目的是继承对象将返回带有有效负载的字符串消息到 UI。 这将通过另一个 object 传递,该 object 具有详细信息并处理序列化/反序列化,其中将包含这些对象的IEnumerable 但是,它需要保存任何 IResult,因为一个 object 中可能有多种类型 它还必须只在该集合中保存 IResults。 这是旨在包含它们的属性:

public IEnumerable<IResult<dynamic>> Results { get; set; }

问题是,使用IEnumerable.Cast()进行强制转换或显式内联强制转换不起作用。 这里dynamic的存在只是我在对object做了同样的事情之后的一次尝试,然后演员阵容也失败了。

我怎样才能让更大的有效负载 object 接受IResult<T>的集合,因为这些都不起作用(首先是因为 T 在上下文中未定义?)

IEnumerable<IResult<T>> Results { get; set; } 
//doesn't work as T is undefined, and if defined at the payload level, would result in a single type
IEnumerable<IResult<dynamic>> Results { get; set; } 
//doesn't work, the cast fails
IEnumerable<IResult<object>> Results { get; set; } 
//doesnt work, the cast fails

无论如何,您要实现的目标与 generics 无关。 重要的是要了解默认情况下 generics 是不变的,例如:

IInterface<One> != IInterface<Two>

即使Two: One

你可以在这里阅读更多关于它的信息

实现您想要的最简单的方法之一是使用对象

public interface IResult
{
     string ResultMessage { get; set; }
     object Data { get; set; }
}

但是,如果您想坚持使用 generics,您可以将IResult接口定义为协变的:

public interface IResult<out T>
{
    string ResultMessage { get; set; }
    T Data { get; } // note that you cannot specify setter in this case
}

然后,像这样定义您的 Result 类:

public class ObjectResult: IResult<object>
{
    private readonly object _data;

    public ObjectResult(object data)
    {
        _data = data;
    }

    public string ResultMessage { get; set; }
    public object Data => _data;
}

public class StringResult : IResult<string>
{
    private readonly string _data;

    public StringResult(string data)
    {
        _data = data;
    }

    public string ResultMessage { get; set; }
    public string Data => _data;
}

现在你可以 go:

IResult<string> stringResult = new StringResult("string");
IResult<object> objectResult = new ObjectResult(new object());

objectResult = stringResult;

Console.WriteLine(objectResult.Data); // prints "string"

老实说,我不认为你可以这样做。 我能想到的唯一方法是拥有 IResult 实现的接口的非通用版本。 如果您需要 data 属性,那么您可以像这样设置它:

public interface IResult
{
    string ResultMessage { get; set; }
    object DataObject { get; }  
}

public interface IResult<T> : IResult
{
    T Data { get; set; }          
}


public class SomeResult<T> : IResult<T>
{
    public string ResultMessage { get; set; }
    public T Data { get; set; }
    public object DataObject => Data == null ? null : (object)Data;
}

然后,您将拥有一个 IEnumerable,您可以循环并获取数据。 您甚至可以做一些技巧,让每个实现都以某种方式对其进行格式化,但这就是我的处理方式。

此外,您可以在此处使用动态而不是 object,但我不完全确定您的最终目标是什么。

在这里试图提供建设性的批评:你为什么要把这两个东西强行放在一个界面中? 你不能用 C# 中的做多个 inheritance,但你可以接口做。

在阅读您的问题时,您的 state:

目的是继承对象将返回带有有效负载的字符串消息到 UI。

所以,这是一个 function,然后

这将通过另一个 object 提供,它具有详细信息并处理序列化/反序列化

这听起来像一个完全不同的 function。 我会拆分接口,比如

public interface IGiveResults
{
    string Results{get; }
}

public interface IGiveData<T>
{
    T Data{get;}
}

不过,这并不能真正解决你的问题,所以我会给你经典的 Stack Exchange 答案,“不要做 A,做 B。没有人做 A。”

如果你的目标是序列化,不要试图强迫你在IEnumerable中的东西返回各种不同的类型,然后试图笨拙地争论那个列表。

相反,请考虑将序列化和反序列化函数添加到接口,并让对象接受序列化程序。 考虑:

public interface ISerializable
{
    void Serialize(Serializer serializer);
    void Deserialize(Deserializer deserializer);
}

您甚至可以将其包含在结果界面中:

public interface IGiveResults : ISerializable
{
    string Results{get;}
}

现在您可以做的是,当您进入序列化或反序列化步骤时,您可以让您的序列化程序自行关闭:

public class Serializer
{
    public void SerializeAll(IEnumerable<IGiveResults> results)
    {
        foreach(var result in results)
        {
            result.Serialize(this);
        }
    }
    public void Serialize<T>(T Data)
    {
        <do your thing>
    }
}

这会翻转你的问题,但我认为更好地完成你想做的事情。 现在,从ISerializable接口继承的子类可以执行以下操作:

public class YourIntSubclass : IGiveResults
{
    public string Results{get; private set;} = "passed!";
    private int[] Data = \\whatever
    public void Serialize(Serializer serializer)
    {
        serializer.Serialize(Data);
    }
}

您只需在序列化程序 class 中实现特定于类型的序列化,您就可以使用 go。 您可以使用模式匹配对序列化进行类型检查,如果您忘记涵盖一个案例,则可以抛出一个方便的异常,例如

public void Serialize<T>(T data)
{
    switch(data)
    {
        case int intData:
            <serialize an int>
            break;
        case float floatData:
            <serialize a float>
            break;
        default:
            throw new System.NotImplementedException("Forgot to implement a serializer for " + data.GetType().ToString());
    }
}

暂无
暂无

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

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