C#: cast to generic interface with base type
Maybe it helps you if I explain why this cast is forbidden: Assume that you have the following function
void myFunc(IValidator<BaseEntity> myValidator) {
myValidator.IsValid(new BaseEntity());
}
This code would compile correctly. Nevertheless, if you passed an OrderValidator
to this function, you would get a run-time exception, because OrderValidator.IsValid
expects an Order, not a BaseEntity. Type safety would no longer be maintained if your cast were allowed.
EDIT: C# 4 allows for generic co- and contravariance, but this would not help in your case, since you use T as an input parameter. Thus, only casting to an IValidator<SomeSubtypeOfOrder>
could be done in a type-safe way.
So, to be clear, you cannot cast OrderValidator
to IValidator<BaseEntity>
because your OrderValidator can only validate orders, not all kinds of BaseEntities. This, however, is what would be expected of an IValidator<BaseEntity>
.
Cast GenericDerived to GenericBase
You cannot cast a Generic<Derived>
to a Generic<Base>
.
Just imagine if you could. You have a List<Wolf>
and cast it to a List<Animal>
. Now you could .Add()
a Sheep
to your List<Animal>
. But wait... now your List<Wolf>
contains a Sheep
. What a mess.
This would only work out if you could make sure that the thing you cast to is read-only in all possible forms. This is was co- and contravariance is all about. It only works for interfaces though.
Casting an object to a generic interface
If I understand the question, then the most common approach would be to declare a non-generic base-interface, i.e.
internal interface IRelativeTo
{
object getRelativeTo(); // or maybe something else non-generic
void setRelativeTo(object relativeTo);
}
internal interface IRelativeTo<T> : IRelativeTo
where T : IObject
{
new T getRelativeTo();
new void setRelativeTo(T relativeTo);
}
Another option is for you to code largely in generics... i.e. you have methods like
void DoSomething<T>() where T : IObject
{
IRelativeTo<IObject> foo = // etc
}
If the IRelativeTo<T>
is an argument to DoSomething()
, then usually you don't need to specify the generic type argument yourself - the compiler will infer it - i.e.
DoSomething(foo);
rather than
DoSomething<SomeType>(foo);
There are benefits to both approaches.
Cast to generic type in C#
Does this work for you?
interface IMessage
{
void Process(object source);
}
class LoginMessage : IMessage
{
public void Process(object source)
{
}
}
abstract class MessageProcessor
{
public abstract void ProcessMessage(object source, object type);
}
class MessageProcessor<T> : MessageProcessor where T: IMessage
{
public override void ProcessMessage(object source, object o)
{
if (!(o is T)) {
throw new NotImplementedException();
}
ProcessMessage(source, (T)o);
}
public void ProcessMessage(object source, T type)
{
type.Process(source);
}
}
class Program
{
static void Main(string[] args)
{
Dictionary<Type, MessageProcessor> messageProcessors = new Dictionary<Type, MessageProcessor>();
messageProcessors.Add(typeof(string), new MessageProcessor<LoginMessage>());
LoginMessage message = new LoginMessage();
Type key = message.GetType();
MessageProcessor processor = messageProcessors[key];
object source = null;
processor.ProcessMessage(source, message);
}
}
This gives you the correct object. The only thing I am not sure about is whether it is enough in your case to have it as an abstract MessageProcessor.
Edit: I added an IMessage interface. The actual processing code should now become part of the different message classes that should all implement this interface.
C# Convert generic, inherited type with interface to base type with same interface
Alright, step by step.
iPoppable<Lion> lions = new Group<Lion>();
Works, because Group
implements iPoppable
and generic parameter T
is the same.
iPoppable<Animal> animals = lions;
Works, because both of them are iPoppable
and Lion
derives from Animal
. More formally, this is an example of covariance.
An object that is instantiated with a more derived type argument is assigned to an object instantiated with a less derived type argument. Assignment compatibility is preserved.
by Microsoft Docs.
Group<Lion> lions2 = lions;
Does not work, because you assign an interface type to a class type. iPoppable
just says that lions
has Lion Pop();
method, no more! By saying Group<Lion> lions2 = lions;
you claim that lions2
is a full-featured Group
object which will have all methods and properties of Group
class. Which is not necessarily true, and that's why compiler complains.
You may help compiler by saying
Group<Lion> lions2 = (Group<Lion>)lions;
because you know for a fact that particularly lions
, although the type is iPoppable
is in fact Group
.
To illustrate what the compiler is afraid of, see the following snippet.
interface iPoppable<out T>
{
T Pop();
}
interface iPushable<in T>
{
void Push(T ag_t);
}
class Program
{
static void Main()
{
// Here, we know the truth, so we cast
iPoppable<bool> group = new Group<bool>();
Group<bool> group2 = (Group<bool>)group; // Possible
// What about here? We also convert iPoppable to Group...
iPoppable<bool> notGroup = new NotGroup<bool>();
Group<bool> notGroup2 = (Group<bool>)notGroup; // Bad... Compiler was right...
notGroup2.HelloGroup = true; // HA! Runtime exception.
// That's what compiler was worrying about.
// System.InvalidCastException: Unable to cast object of
// type 'NotGroup`1[System.Boolean]' to type 'Group`1[System.Boolean]
}
}
class Group<T> : iPoppable<T>, iPushable<T>
{
public void Push(T ag_t) { }
public T Pop() { return default(T); }
public bool HelloGroup { get; set; }
}
class NotGroup<T> : iPoppable<T>, iPushable<T>
{
public void Push(T ag_t) { }
public T Pop() { return default(T); }
public bool HelloNotGroup { get; set; }
}
Generic type is cast to base class
The type parameter T
is inferred at compile time, that's why it's different when you call a method with a parameter Base b1
and Derived b2
(using var
simply makes the type of the variable the same as the type of the expression used to initialize it).
The simplest way to make it happen at run time is to cast the parameter to dynamic
SomeMethod((dynamic)b1);
This will basically run the compiler at run time and perform overload resolution and type inference with run time types instead of compile time types.
In general what you're asking about is called double dispatch - choosing the method to call based on both the runtime type of the object whose method you call, and the run time type of the parameter. A traditional way to achieve it is to use the visitor pattern.
C# : Downcasting Generic object with derived interfaces to the base interface
Your list is List<IAlarmItem<IConfigAlarmBase>>
and you try to add an AlarmItemRangedBase<IConfigAlarmRanged>
, so you have type mismatch even if IConfigAlarmRanged
inherits from IConfigAlarmBase
because IAlarmItem<IConfigAlarmBase>
and IAlarmItem<IConfigAlarmRanged>
are not the same type : they are two distinct closed constructed generic types and you can't cast from one to the other.
For example, List<string>
and List<int>
are distinct types, there is no hierarchy between and no side-casting is available.
Generics -Open and closed constructed Types
About the lack of true generic polymorphism and the missing diamond operator in C#
Unable to cast object of generic type to generic interface C#
That's a lot of levels of indirection you got there...
Here's the issue:
public abstract class Rule<TDmModel, TMiddle, TDb> : IRule<TDmModel, TDb>
where TDmModel : IDmModel
where TDb : IDbModel
public class RuleA : Rule<DmModel, Middle, DbMode>
public class RuleB : RuleA
...
var ruleB = (IRule<IDmModel, IDbModel>)new RuleB();
RuleB implements IRule<DmModel, DbMode>
This cannot be cast to IRule<IDmModel, IDbModel>. C# does not support this type of casting. For the same reason, you cannot do List<object> b = (List<object>)new List<string>();
(Gives "Cannot convert type 'System.Collections.Generic.List<string> to System.Collections.Generic.List<object>.")
This is an issue with covariance.
Here is some more information from Microsoft on the subject: https://learn.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance
Related Topics
How to Use C# 8 with Visual Studio 2017
C# Datetimes: Conversion for Different Time Zones
Check If Datetime Instance Falls in Between Other Two Datetime Objects
Comparing 2 Objects and Retrieve a List of Fields with Different Values
In C# Why Can't a Conditional Operator Implicitly Cast to a Nullable Type
Get the Decimal Part from a Double
How to Translate a List<String> into a SQLparameter for a SQL in Statement
How to Hide Public Methods from Intellisense
Linq to Xml - Update/Alter the Nodes of an Xml Document
Cannot Deserialize JSON Array into Type - JSON.Net
Print HTML Document from Windows Service Without Print Dialog
Combobox.Selectedtext Doesn't Give Me the Selectedtext
Are Timers and Loops in .Net Accurate
Converting Long String of Binary to Hex C#
Dynamically Cross-Join Multiple Different-Size Collections Together in Linq (C#)
C# Httpwebrequest the Underlying Connection Was Closed: an Unexpected Error Occurred on a Send