如何将派生对象添加到从同一接口继承但使用泛型的对象集合中?
How do I add a derived object to a collection of objects which inherit from the same interface, but use generics?
我对 C# 中的泛型有些陌生。我有一个 IResult
接口,它具有以下属性:
public interface IResult<T>
{
string ResultMessage { get; set; }
T Data { get; set; }
}
目的是继承对象将向 UI 返回带有有效负载的字符串消息。这将通过另一个对象提供,该对象具有详细信息和句柄 serialize/deserialization,其中将包含这些对象的 IEnumerable
。 但是,它需要保存任何 IResult,因为一个对象中可能有多种类型。它还必须只在该集合中保存 IResults。这是旨在包含它们的 属性:
public IEnumerable<IResult<dynamic>> Results { get; set; }
问题是,使用 IEnumerable.Cast()
或显式内联转换不起作用。 dynamic
的存在只是我在对 object
做了同样的事情之后的一次尝试,然后演员表也失败了。
我怎样才能让我的更大的负载对象接受 IResult<T>
的集合,因为 none 这些工作(第一个是因为 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
老实说,我不认为你可以这样做。我能想到的唯一方法是拥有 IResult 实现的接口的非通用版本。如果您需要数据 属性,那么您可以这样设置:
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,您可以循环访问它并获取其数据。您甚至可以做一些技巧,让每个实现都以某种方式对其进行格式化,但这就是我的处理方式。
此外,您可以在此处使用 dynamic 而不是 object,但我不完全确定您的最终目标是什么。
在这里尝试提供建设性的批评:你为什么要将这两个东西强加到一个界面中?您不能在 C# 中使用 classes 进行多重继承,但是您 可以 使用 接口进行多重继承。
在阅读您的问题时,您声明:
The purpose being that inheriting objects will be returning a string message with a payload to the UI.
所以,这是一个功能,然后
This will be delivered via another object which has detailed information and handles serialize/deserialization
这听起来像是一个完全不同的功能。我会拆分接口,比如
public interface IGiveResults
{
string Results{get; }
}
和
public interface IGiveData<T>
{
T Data{get;}
}
不过,这并不能真正解决您的问题,所以我会给您 classic Stack Exchange 答案,“不做 A,做 B。没有人做 A。”。 =21=]
如果您的目标是序列化,请不要试图将 IEnumerable
中的内容强加到 return 各种不同的类型,然后再尝试笨拙地争论该列表。
相反,考虑向接口添加序列化和反序列化函数,并让对象接受序列化程序。考虑:
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
接口继承的子 classes 可以做到:
public class YourIntSubclass : IGiveResults
{
public string Results{get; private set;} = "passed!";
private int[] Data = \whatever
public void Serialize(Serializer serializer)
{
serializer.Serialize(Data);
}
}
您只需在序列化程序中实现特定于类型的序列化 class 就可以了。您可以使用模式匹配对序列化进行类型检查,如果您忘记涵盖一个案例,例如
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());
}
}
无论如何,你想要实现的都与泛型无关。重要的是要理解默认情况下泛型是不变的,例如:
IInterface<One> != IInterface<Two>
即使Two: One
.
您可以阅读更多相关信息here
实现您想要的最简单方法之一是使用对象
public interface IResult
{
string ResultMessage { get; set; }
object Data { get; set; }
}
但是,如果您想坚持使用泛型,您可以将 IResult
接口定义为协变的:
public interface IResult<out T>
{
string ResultMessage { get; set; }
T Data { get; } // note that you cannot specify setter in this case
}
然后,像这样定义结果 类:
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;
}
现在您可以开始了:
IResult<string> stringResult = new StringResult("string");
IResult<object> objectResult = new ObjectResult(new object());
objectResult = stringResult;
Console.WriteLine(objectResult.Data); // prints "string"
我对 C# 中的泛型有些陌生。我有一个 IResult
接口,它具有以下属性:
public interface IResult<T>
{
string ResultMessage { get; set; }
T Data { get; set; }
}
目的是继承对象将向 UI 返回带有有效负载的字符串消息。这将通过另一个对象提供,该对象具有详细信息和句柄 serialize/deserialization,其中将包含这些对象的 IEnumerable
。 但是,它需要保存任何 IResult,因为一个对象中可能有多种类型。它还必须只在该集合中保存 IResults。这是旨在包含它们的 属性:
public IEnumerable<IResult<dynamic>> Results { get; set; }
问题是,使用 IEnumerable.Cast()
或显式内联转换不起作用。 dynamic
的存在只是我在对 object
做了同样的事情之后的一次尝试,然后演员表也失败了。
我怎样才能让我的更大的负载对象接受 IResult<T>
的集合,因为 none 这些工作(第一个是因为 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
老实说,我不认为你可以这样做。我能想到的唯一方法是拥有 IResult 实现的接口的非通用版本。如果您需要数据 属性,那么您可以这样设置:
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,您可以循环访问它并获取其数据。您甚至可以做一些技巧,让每个实现都以某种方式对其进行格式化,但这就是我的处理方式。
此外,您可以在此处使用 dynamic 而不是 object,但我不完全确定您的最终目标是什么。
在这里尝试提供建设性的批评:你为什么要将这两个东西强加到一个界面中?您不能在 C# 中使用 classes 进行多重继承,但是您 可以 使用 接口进行多重继承。
在阅读您的问题时,您声明:
The purpose being that inheriting objects will be returning a string message with a payload to the UI.
所以,这是一个功能,然后
This will be delivered via another object which has detailed information and handles serialize/deserialization
这听起来像是一个完全不同的功能。我会拆分接口,比如
public interface IGiveResults
{
string Results{get; }
}
和
public interface IGiveData<T>
{
T Data{get;}
}
不过,这并不能真正解决您的问题,所以我会给您 classic Stack Exchange 答案,“不做 A,做 B。没有人做 A。”。 =21=]
如果您的目标是序列化,请不要试图将 IEnumerable
中的内容强加到 return 各种不同的类型,然后再尝试笨拙地争论该列表。
相反,考虑向接口添加序列化和反序列化函数,并让对象接受序列化程序。考虑:
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
接口继承的子 classes 可以做到:
public class YourIntSubclass : IGiveResults
{
public string Results{get; private set;} = "passed!";
private int[] Data = \whatever
public void Serialize(Serializer serializer)
{
serializer.Serialize(Data);
}
}
您只需在序列化程序中实现特定于类型的序列化 class 就可以了。您可以使用模式匹配对序列化进行类型检查,如果您忘记涵盖一个案例,例如
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());
}
}
无论如何,你想要实现的都与泛型无关。重要的是要理解默认情况下泛型是不变的,例如:
IInterface<One> != IInterface<Two>
即使Two: One
.
您可以阅读更多相关信息here
实现您想要的最简单方法之一是使用对象
public interface IResult
{
string ResultMessage { get; set; }
object Data { get; set; }
}
但是,如果您想坚持使用泛型,您可以将 IResult
接口定义为协变的:
public interface IResult<out T>
{
string ResultMessage { get; set; }
T Data { get; } // note that you cannot specify setter in this case
}
然后,像这样定义结果 类:
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;
}
现在您可以开始了:
IResult<string> stringResult = new StringResult("string");
IResult<object> objectResult = new ObjectResult(new object());
objectResult = stringResult;
Console.WriteLine(objectResult.Data); // prints "string"