I have the following code where I want to downcast to an interface with generic but I get Run-time exception: Unable to cast object of type 'FinalAssociator' to type 'IAssociator`1[Common]'.
public interface ICommon
{
string Name {get;set;}
}
public class Common : ICommon
{
public string Name {get;set;}
}
public class FinalCommon : Common {}
public interface IAssociator<T> where T : ICommon
{
void HandleEvent(T data);
}
public abstract class Associator<T> : IAssociator<T> where T : ICommon
{
public abstract void HandleAnotherEvent(T data);
public void HandleEvent(T data)
{
HandleAnotherEvent(data);
}
}
public class FinalAssociator : Associator<FinalCommon>
{
public override void HandleAnotherEvent(FinalCommon data)
{
Console.WriteLine(data.Name);
}
}
var x = new FinalAssociator();
var y = new FinalCommon { Name = "John" };
var z = (IAssociator<Common>)x;
z.HandleEvent(y);
You can't do this because it could lead to runtime errors due to invalid types, which is one of the things generics is intended to prevent. Consider what would happen if the compiler allowed your code. You have:
z.HandleEvent(y);
Here y is an instance of FinalCommon, which won't present a problem. However, what if you instead passed in something else, like:
z.HandleEvent(new Common());
This would result in your passing an instance of something that isn't FinalCommon to your method that is definitely expecting an instance of FinalCommon. This would be illegal, and the compiler prevents you from getting into this situation.
FinalAssociatior inherits from Associator<FinalCommon>. Its HandleAnotherEvent method expects an argument of type FinalCommon.
If you could cast an instance of it as IAssociator<Common> then you'd be able to pass an argument of type Common to it, even though the class expects FinalCommon.
var finalAssociator = new FinalAssociator();
var commonAssociator = (IAssociator<Common>)finalAssociator; // can't do this
// You'd be able to do this because the interface allows it, but it doesn't
// make sense because the object is a FinalAssociator
// and it doesn't take this argument.
commonAssociator.HandleAnotherEvent(new Common());
As written, the compiler is unable to determine that this is invalid, which is why you get a runtime error. (Resharper provides a warning that this may fail at runtime.)
Related
I have the following base class (omitted version):
class BaseClass
{
}
I create the following derived classes:
class DataPreparationClass<T> : BaseClass
{
}
class DataClass<T,U> : DataPreparationClass<T>
{
public virtual void Start<U>(U arg)
{}
}
class DataClassMain : DataClass<InputData,Data>
{
public override void Start(Data argument)
{
base.Start(argument);
}
}
class DataClassAux : DataClass<InputData,AuxData>
{
public override void Start(AuxData argument)
{
base.Start(argument);
}
}
I have a List<BaseClass> containing various derived instances (there are more derived types) and I intend to call their Start method respectively:
List<BaseClass> instances = GetAllInstance();
foreach(BaseClass instance in instances)
{
object arg = GetArgsForInstance(instance);
// instance.Start(arg); //can't call this
}
However, as their common base is the BaseClass, I can't call Start without casting to...basicly every possible type as their types are unknown at the processing.
If I use dynamic:
((dynamic)target).Start(new Data("starting")); //target is of type DataClassMain<InputData,Data>
I get an exception:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'The best
overloaded method match for
'Client.DataClass<InputData,Data>.Start(Data)' has some invalid
arguments'
So how should I call the unknown method?
So, the most straight forward answer to your question would be to use pattern matching to call the start method.
List<BaseClass> instances = GetAllInstance();
foreach(BaseClass instance in instances)
{
object arg = GetArgsForInstance(instance);
switch(instance){
case DataClassMain d : d.Start((Data)arg); break;
case DataClassAux a : a.Start((AuxData)arg);break;
default: throw new Exception();
}
}
But I do get the feeling this is an convoluted and inappropriate inheritance chain, and you should really consider using a factory and/or strategy pattern instead.
It's assumed that GetArgsForInstance allways will return the correct type with respect to the type it receives as an argument, and that the return types (Data, AuxData and so on) share a common base type. So we could do the type resolution directly with a signature of T GetArgsForInstance<T>(BaseClass b). That way you can make sure you get args of the right type before you return it.
Since the Start overrides just pass along the call generic types, so the overrides in DataClassMain and DataClassAux are unnecessary.
If we modify DataClass a bit we can then do it like this:
class DataClass<T,U> : DataPreparationClass<T>
{
public virtual void Start(U arg)
{
//Do somethin with arg
}
public void Call(Func<BaseClass,U> f){
U data = f.Invoke(this);
Start(data);
}
}
and invoke it with
List<BaseClass> instances = GetAllInstance();
foreach(BaseClass instance in instances)
{
switch(instance)
{
case DataClassMain d : d.Call(GetArgsForInstance<Data>); break;
case DataClassAux a : a.Call(GetArgsForInstance<AuxData>);break;
default: throw new Exception();
}
}
The reason this is preferable is that we can let the compiler ensure that we only pass the appropriate types to the different methods, no casting needed.
But again, such a convoluted inheritance chain should almost always be avoided.
I would say, your questions shows multiple flaws in your model:
by definition of your classes, there is no polymorphism inbetween you Start() methods : Start(Data) do not override Start<U>(U)
by definition of your GetArgsForInstance() method, you have lost the type information you need.
I would add that Classes that are called Classes and Data that are called Data and that are parameterized with their content are way too generic.
That saying, your question implies that you are not wanting to fix those flaws, maybe they are out of your control, so you have to live with it :
instead of not loosing the Type information, you ask for a way to retrieve it.
instead of using polymorphism to retrieve the type from the best place to retrieve do so (in my opinion), which is the Start method itself, you ask for a way to retrieve it in the calling code.
So, what I would try to do is :
rework your GetArgsForInstance() method to be able not to loose this information, for instance, replace it by an object, something like :
class DataClassMain : DataClass<InputData,Data>
{
public override void Start(ArgumentProvider argumentProvider)
{
Data argument = argumentProvider.getArgumentAsData(argumentProvider);
base.Start(argument);
}
}
if not possible, retrieve the types from the inside of the derived classes, for instance something like :
public class DataClassMain : DataClass<InputData,Data>
{
public override void Start(object arg)
{
base.Start(arg);
Data argAsData = (Data) arg;
}
}
if not possible, that means you already have a set of constraint that is making your code hard to maintain, so let's go for a messy reflective thing, but you have to be aware that there is no polymorphism involved and get rid of your 'override' and 'virtual' modifier on Start() methods. Here is a fully working program, which output is :
DataClassMain
DataClassAux
public static void Main(string[] args)
{
List<BaseClass> instances = GetAllInstance();
foreach(BaseClass instance in instances)
{
object value = GetArgsForInstance(instance);
messyInvoke(instance, value);
}
}
private static void messyInvoke(BaseClass instance, object value)
{
MethodInfo method = instance.GetType().GetMethod("Start");
if (method != null)
{
ParameterInfo[] parametersInfos = method.GetParameters();
if (parametersInfos.Length == 1)
{
object[] paramArray = {value};
method.Invoke(instance, paramArray);
}
}
}
public class BaseClass{
public virtual Type GetTypeOfArgs()
{
return typeof(Toto);
}
}
public class DataPreparationClass<T> : BaseClass {}
public abstract class DataClass<T> : DataPreparationClass<T>
{
}
public class DataClassMain : DataClass<Toto>
{
public void Start(Data arg)
{
Console.WriteLine("DataClassMain");
}
}
public class DataClassAux : DataClass<Toto>
{
public void Start(AuxData argument)
{
Console.WriteLine("DataClassAux");
}
}
private static object GetArgsForInstance(BaseClass isntance)
{
if (isntance is DataClassMain)
return new Data();
if (isntance is DataClassAux)
return new AuxData();
throw new ArgumentException();
}
private static List<BaseClass> GetAllInstance()
{
return new List<BaseClass> {new DataClassMain(), new DataClassAux()};
}
public class Toto{}
public class DataClassInputData
{
}
public class Data : DataClassInputData
{
}
public class AuxData : DataClassInputData
{
}
Apologies if this is duplicate, I'm not familiar enough with the correct terminology to know if this is asked elsewhere. I'm new to interfaces and am creating some sample code to see what kind of helpful things they can achieve.
I have a method that returns two possible classes that both implement the same interface. However, I can only access the properties from the parent class and not the sub-class, and have failed to find an explanation. I realise my entire approach may be flawed and will accept that advise also.
This is better explained with an example (NetFiddle) and questions...
using System;
public interface IAb
{
int Prop1 { get; set; }
}
public class A : IAb
{
public int Prop1 { get; set; }
}
public class B : A, IAb
{
public string Prop2 { get; set; }
}
public class Program
{
static class MyMethods
{
public static IAb CreateObject(Type t)
{
if (t == typeof(A))
{
return new A() {Prop1 = 123};
}
else
{
return new B() {Prop1 = 456, Prop2 = "Foo"};
}
}
}
public void Main()
{
IAb AorB = MyMethods.CreateObject(typeof(B));
Console.WriteLine(AorB.Prop1);
if (AorB is B)
{
// fails
// Console.WriteLine((B)AorB.Prop2); // 'IAb' does not contain a definition for 'Prop2'
// works
B newVar = (B)AorB;
Console.WriteLine(newVar.Prop2);
}
}
}
Based on the above:
Why does AorB.Prop1 work, but not AorB.Prop2 without an explicit cast?
Does the above mean that an interface should always be cast to a class before reading it's properties?
Is there a better way to specify the return type of the example method? The current approach feels error-prone because this (albeit unlikely code) would cause an error: var AorB = (B) MyMethods.CreateObject(typeof(A));
You may use pattern matching with is operator to make it more clear
IAb AorB = MyMethods.CreateObject(typeof(B));
Console.WriteLine(AorB.Prop1);
if (AorB is B b)
{
Console.WriteLine(b.Prop2);
}
Your failed line becomes correct after using right parenthesis
Console.WriteLine(((B)AorB).Prop2);
There is also no need to inherit B class from both A class and IAB interface. Since A already implements IAB, you can simply use public class B : A
Is there a better way to specify the return type of the example
method?
Make the CreateObject method generic and use constraints to restrict T type parameter to class, which implements IAb interface and has a parameterless constructor (this's what new() means exactly)
public static T CreateObject<T>() where T : IAb, new()
{
return new T { Prop1 = 123 };
}
and invoke it in the following way
IAb AorB = MyMethods.CreateObject<B>();
But in this case you can set only properties, defined in IAb interface, not the B class specific.
Why does AorB.Prop1 work, but not AorB.Prop2 without an explicit cast?
Because the property selector has precedence over the type cast. Just add parentheses:
Console.WriteLine(((B)AorB).Prop2);
Does the above mean that an interface should always be cast to a class before reading it's properties?
No.
Is there a better way to specify the return type of the example method? The current approach feels error-prone because this (albeit unlikely code) would cause an error: var AorB = (B) MyMethods.CreateObject(typeof(A));
You could use generics:
T CreateObject<T>() where T : new()
{
return new T();
}
However, that does not provide you with compile-time access to properties as in your example. You could slightly improve by providing versions of the method with appropriate constraints:
T CreateObject<T>() where T : IAb, new()
{
var result = new T();
result.Prop1 = 123;
return result;
}
I have created this simple generic interface:
public interface IInitializerSettings<in ViewerType> where ViewerType : Component
{
void Apply(ViewerType dataViewer);
}
And added an implementation for it:
public class MenuSettings : IInitializerSettings<CustomGridLayout>
{
public void Apply(CustomGridLayout dataViewer)
{
Debug.Log("Applied");
}
}
public class CustomGridLayout : CustomLayout
{
// The implementation code
}
Now I try to use it like that:
public IInitializerSettings<CustomLayout> GetDefaultSettings()
{
return new MenuSettings();
}
But I get this error "Cannot convert type MenuSettings to return type IInitializerSettings"
I don't understand why it isn't allowed, CustomGridLayout inherits CustomLayout.
All I could find is this question, but this solution doesn't work for me (I can't use the out keyword).
The reason you cannot do this is because for a contravariant interface (specified by your use of in for the generic type parameter) you cannot implicitly convert it to an instance of a less derived type. I think the bullet points in the docs explains it fairly ok, if you think in terms of IEnumerable<T> (covariant) and Action<T> (contravariant).
As Selvin mentions in the comments the Apply method in MenuSettings expects an instance of CustomGridLayout, so trying to cast MenuSettings to IInitializerSettings<CustomLayout> is not possible because public void Apply(CustomGridLayout dataViewer) cannot handle a CustomLayout as input. Let me give an example:
public class CustomLayout
{
public void SetupCustomLayout() { ... }
}
public class CustomGridLayout : CustomLayout
{
public void SetupGrid() { ... }
}
public class MenuSettings : IInitializerSettings<CustomGridLayout>
{
public void Apply(CustomGridLayout dataViewer)
{
dataViewer.SetupGrid();
}
}
// Later in the code...
var menuSettings = new MenuSettings();
// This cast is what GetDefaultSettings() is trying to do
var genericSettings = (IInitializerSettings<CustomLayout>)menuSettings;
var layout = new CustomLayout();
// Looking at the type of 'genericSettings' this following line should be possible
// but 'MenuSettings.Apply()' is calling 'dataViewer.SetupGrid()' which doesn't exist
// in 'layout', so 'layout' is not a valid input
genericSettings.Apply(layout);
So in relation to the docs you have defined IInitializerSettings<ViewerType> as a contravariant interface, but are trying to use it as a covariant interface - which is not possible.
I have a base service class with virtual method that sets the properties of an object and returns that object.
Then i have one more service which derived from the base service and also overrides the base method. In overriden method, the derived service executes base.DowWork() to set common properties, and then also sets additional properties.
So based on articles here and here I was able to do this using generics.
public interface IResult
{
}
public class BaseResult : IResult
{
public string CommonProperties { get; set; }
}
public class AdditionalResult : BaseResult
{
public string AdditionalProperties { get; set; }
}
public interface IService<T> where T : IResult
{
T DoWork();
}
public class BaseService<T> : IService<T> where T : BaseResult, new()
{
public virtual T DoWork()
{
var t = new T();
t.CommonProperties = "Some Value";
return t;
}
}
public class AdditionalService : BaseService<AdditionalResult>
{
public override AdditionalResult DoWork()
{
var addtionalResult = base.DoWork();
addtionalResult.CommonProperties = "Override value that was set by BaseService";
addtionalResult.AdditionalProperties = "Set additional properties";
return addtionalResult;
}
}
So far so good
Now i want to create a Factory method that will return the instance of a service based on some type. The application will use the factory to get service instance and call DoWork() like below
class Program
{
static void Main()
{
var factory = new MyFactory();
var service = factory.GetService(0);
var iresult = service.DoWork();
// do something here with IResult
}
}
below is the factory method
public class MyFactory
{
public IService<IResult> GetService(int someType)
{
if (someType == 0)
{
return (IService<IResult>)new BaseService<BaseResult>();
}
if (someType == 1)
{
return (IService<IResult>)new AdditionalService();
}
// note I may have more types and services here. But for simplicity i am using only 2
throw new NotSupportedException();
}
}
However i am not able to figure out what should be the signature of this factory method? Based on suggestions here I'm casting service instance but while executing the application I am getting runtime exception
Unable to cast object of type
'BaseService 1[BaseResult]' to
type 'IService 1[IResult]'
if i don't cast the service instance in the Factory then i get compile time error
Cannot implicitly convert type 'BaseService' to
'IService'. An explicit conversion exists (are you missing a
cast?)
See SO question Understanding Covariant and Contravariant interfaces in C#.
You want to use covariance (out keyword). If you add it to your IService interface generic type it works as expected.
public interface IService<out T> where T : IResult
I know SO prefers not to post links but I can't possibly write anything more or better than already answered in that question.
In Java this is valid:
class Class1<T extends OtherType> { T t; ... }
//Inside other class that has no relation to Class1
private Class1 class1; //No type needed
public void someOtherFunction (Class1 class1) //Works
{
this.class1 = class1; //May warn about invalid type casting
class1.someFunction();
}
C# demands a type under the same conditions:
class Class1<T> where T : OtherType { T t; ... }
//Inside other class that has no relation to Class1
private Class1<TYPE DEMANDED> class1; //I don't know generic what type, and honestly it doesn't matter.
public void someOtherFunction (Class1 class1) //This is the definition I want to use, but C# Demands a type to class1
public void someOtherFunction<T> (Class1<T> class1) where T : OtherType //C# Demands a type to class1 so I must specify a T
{
this.class1 = class1;
class1.someFunction();
} //This function is valid but then it still needs the type to store it.
Is there any way to omit the type? It isn't necessary to know the type so why is it required? I can't make Class1 of type OtherType, because the point of the generic is to have an unknown type that extends the base of OtherType. I can work around it, but this is definitely the most efficient solution, if it were Java and I will have to type cast once a frame for multiple objects if this does not work, which I am worried will add up fast.
Actual code, per request:
public abstract class Weapon { ... }
public abstract class WeaponProxy<T> : MonoBehaviour where T : Weapon
{
protected T weapon;
public virtual void Update()
{
...
holdingPlayer = player.getHUD().showPickup(player, this);
...
}
public abstract class GunProxy<T> : WeaponProxy<T> where T : Gun
{
}
public abstract class Weapon
{
private string weaponName;
private string weaponIdentifier;
private Player isHolding;
public string getWeaponName () { return weaponName; }
public Weapon(string weaponName, string weaponIdentifier)
{
this.weaponName = weaponName;
this.weaponIdentifier = weaponIdentifier;
}
public void playerPickedUp (Player player)
{
this.isHolding = player;
}
public void playerDropped ()
{
this.isHolding = null;
}
}
public class Gun : Weapon
{
...
}
public class HUD : MonoBehaviour
{
private WeaponProxy weapon; //C# Needs a type. Underlined in red as error
public PlayerProxy showPickup<T> (PlayerProxy player, WeaponProxy<T> weapon) where T : Weapon
{
this.weapon = weapon;
textPickupWeapon.text = "Hold '" + player.getPlayer().getControlScheme().getControlText(ControlScheme.CONTROLS.Interact) + "' to pick up " + weapon.getWeapon().getWeaponName();
...
}
}
Your java code is "valid" because all generics in java are actually non-generics, since java's generics are only a compile time trick, with no runtime support whatsoever.
For the java runtime, type A<T> is actually A with no type parameters, because the java runtime doesn't actually support generics at all.
In contrast, the .NET CLR has built-in support for runtime generic types, and hence it differentiates between a type A and a generic type A<T>.
In C#, if you want a non-generic version of type Class1, simply declare it:
class Class1
{
//Whatever members that don't require a type parameter,
void SomeFunction() { /* ... */ } // Please use proper casing.
}
then, if you need a generic version of this class:
class Class1<T>: Class1
{
T Content { get; set; }
}
Now you will be able to have a member of type Class1 in any other class
class Example
{
Class1 instance; // Valid
public void someOtherFunction (Class1 class1) //Works
{
this.instance = class1; //Does not warn about anything because this is type safe.
class1.SomeFunction(); // Works
var content = class1.Content // Compile Error: type class1 does not have such member "Content"
}
}
Notice how the C# approach is much safer, if you're using the non generic version, you can only access class members that are defined in the non generic version and do not require a type parameter. In contrast, java is totally unsafe, and may produce horrible runtime errors due to lack of real type safety in generics.