I'm somewhat new to generics in C#. I have a IResult
interface which has the following properties:
public interface IResult<T>
{
string ResultMessage { get; set; }
T Data { get; set; }
}
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, which will contain an IEnumerable
of these objects. However, it needs to hold any IResult, as there may be multiple types in the one object. It must also only hold IResults in that collection. This is the property which is intended to contain them:
public IEnumerable<IResult<dynamic>> Results { get; set; }
The problem is, casting with IEnumerable.Cast()
or an explicit inline cast doesn't work. The presence of dynamic
here was just an attempt by me after doing the same thing with object
and the cast failing then as well.
How can I have my larger payload object accept a collection of IResult<T>
, since none of these work (the first because T is undefined in the context?)
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
What you want to achieve is not related to generics in any case. It is important to understand that by default generics are invariant, e.g.:
IInterface<One> != IInterface<Two>
even if Two: One
.
You can read more about it here
One of the simplest ways to achieve what you want is to use objects
public interface IResult
{
string ResultMessage { get; set; }
object Data { get; set; }
}
If, however, you want to stick to generics, you can define your IResult
interface as covariant:
public interface IResult<out T>
{
string ResultMessage { get; set; }
T Data { get; } // note that you cannot specify setter in this case
}
Then, define your Result classes like this:
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;
}
Now you're able to go:
IResult<string> stringResult = new StringResult("string");
IResult<object> objectResult = new ObjectResult(new object());
objectResult = stringResult;
Console.WriteLine(objectResult.Data); // prints "string"