Co/Contra-Variance with input and output - c#

I have an issue with co/contra-variance. I understand you can't have both input and output. So here is a simple example:
public interface A<T>
{
T Object {get;set;}
}
public interface B
{
// Some stuff
}
public class BImplementor : B
{ }
public class Implementor : A<BImplementor> {}
Suppose you have these classes and I'm wanting to write a method like this
public void Command(B obj)
{
var a = (A<B>)Unity.Resolve(typeof(A<>).MakeGenericType(obj.GetType());
a.Object = obj;
}
I'm using Unity to resolve a A of the specifc implementor of B (specifically Implementor), but all I know about it is that it is an A<B>. I don't know of a way to do this directly and I don't think it is actually possible, but does anyone know of a workaround to simulate what I'm trying to do.

As you said, you cannot have both input and output, so let's change A<T> to A<in T> so Command can assign obj to the Object property:
public interface A<in T>
{
void SetObject(T obj);
}
public interface B { }
public class BImplementor : B { }
public class Implementor : A<BImplementor>
{
public void SetObject(BImplementor t) { ... }
}
The Command method essentially does this:
public void Command(B obj)
{
A<B> a = (A<B>)new Implementor();
a.SetObject(obj);
}
But this cast can never succeed, because A<B>.SetObject must accept any B as input, while Implementor.SetObject accepts only BImplementor objects as input!
Since you now that you'll only ever pass a BImplementor to A<B>.SetObject, you can work around the problem using reflection.
Workaround 1:
public void Command1(B obj)
{
object a = Unity.Resolve(typeof(A<>).MakeGenericType(obj.GetType());
a.GetType().GetMethod("SetObject").Invoke(a, new object[] { obj });
}
Workaround 2:
public void Command(B obj)
{
this.GetType()
.GetMethod("Command2")
.MakeGenericMethod(obj.GetType())
.Invoke(this, new object[] { obj });
}
public void Command2<T>(T obj) where T : B
{
A<T> a = Unity.Resolve<A<T>>();
a.SetObject(obj);
}

Related

Is this the only way to have the functionality of abstract static methods?

I'm working on a framework right now and the motto is "no redundancy" and "I don't want to know the vendor specifics" so most things are handled through Interfaces and Generic classes. Now I had the situation where I have an abstract class that wants to match things depending on it's own Enum variable se but it shouldn't have to know how the vendor provides a relatable variable to be matched to se. The vendor could have decided an integer, an Enum or a string would be the best to save that information but honestly I don't want to know.
So I thought well no problem have an abstract static method that must be provided by every implementation of a wrapper to compare se with the vendor specific way of saving that information.
//The original version I wanted to be possible
public abstract class AbstractGenericClass<TWrapper<T>, T> where TWrapper : AbstractGenericWrapper<T> {
protected TWrapper tWrapper;
//our SomeEnum se is somehow relatable to every T
//but we don't want to know how
protected SomeEnum se = ...;
//called on Start
public void Start() {
List<T> ts = FindObjectsOfType<T>;
foreach (T t in ts) {
if(T.Compare(t, this.se)) {
tWrapper = new TWrapper(t);
}
}
}
}
public abstract class AbstractGenericWrapper<T> {
T _t;
public AbstractGenericWrapper(T t) {
_t = t;
}
public static abstract bool Compare(T t, SomeEnum someEnum);
}
public class ConcreteNongenericWrapper : AbstractGenericWrapper<VendorSpecificImplementation> {
public static bool Compare(VendorSpecificImplementation t, SomeEnum someEnum) {
return t.vendorVariable.toLower().Equals(Enum.GetValues(typeof(someEnum), someEnum));
}
}
public class OtherConcreteNongenericWrapper : AbstractGenericWrapper<OtherVendorSpecificImplementation> {
public static bool Compare(OtherVendorSpecificImplementation t, SomeEnum someEnum) {
return t.otherVendorVariable % 3 == (int) someEnum;
}
}
public class SomeImplementation {
public static void main() {
AbstractGenericClass<ConcreteNongenericWrapper<ConcreteNongeneric>, ConcreteNongeneric> foo
= new AbstractGenericClass<ConcreteNongenericWrapper<ConcreteNongeneric>, ConcreteNongeneric>();
AbstractGenericClass<OtherConcreteNongenericWrapper<OtherConcreteNongeneric>, OtherConcreteNongeneric> bar
= new AbstractGenericClass<OtherConcreteNongenericWrapper<OtherConcreteNongeneric>, OtherConcreteNongeneric>();
foo.Start();
bar.Start();
}
}
I found out that that isn't possible and I wanted to know if this version down below is the best/only way of doing it? It has redundancy and I don't like it and it is longer.
//An attempt at a solution:
public abstract class AbstractGenericClass<TWrapper<T>, T> where TWrapper : AbstractGenericWrapper<T> {
protected TWrapper tWrapper;
//our SomeEnum se is somehow relatable to every T
//but we don't want to know how
protected SomeEnum se = ...;
//called on start
public abstract void Start();
}
public abstract class AbstractGenericWrapper<T> {
T _t;
public AbstractGenericWrapper(T t) {
_t = t;
}
}
public class ConcreteNongenericClass : AbstractGenericClass<VendorSpecificImplementation> {
//called on start
public override void Start() {
List<VendorSpecificImplementation> ts = FindObjectsOfType<VendorSpecificImplementation>;
foreach (VendorSpecificImplementation t in ts) {
if(t.vendorVariable.toLower().Equals(Enum.GetValues(typeof(someEnum), someEnum))) {
tWrapper = new ConcreteNongenericWrapper(t);
}
}
}
}
public class ConcreteNongenericWrapper : AbstractGenericWrapper<VendorSpecificImplementation> {
}
public class OtherConcreteNongenericClass : AbstractGenericClass<OtherVendorSpecificImplementation> {
//called on start
public void Start() {
List<OtherVendorSpecificImplementation> ts = FindObjectsOfType<OtherVendorSpecificImplementation>;
foreach (OtherVendorSpecificImplementation t in ts) {
if(t.otherVendorVariable % 3 == (int) someEnum) {
tWrapper = new OtherConcreteNongenericWrapper(t);
}
}
}
}
public class OtherConcreteNongenericWrapper : AbstractGenericWrapper<OtherVendorSpecificImplementation> {
}
public class SomeImplementation {
public static void main() {
AbstractGenericClass<ConcreteNongenericWrapper<ConcreteNongeneric>, ConcreteNongeneric> foo
= new AbstractGenericClass<ConcreteNongenericWrapper<ConcreteNongeneric>, ConcreteNongeneric>();
AbstractGenericClass<OtherConcreteNongenericWrapper<OtherConcreteNongeneric>, OtherConcreteNongeneric> bar
= new AbstractGenericClass<OtherConcreteNongenericWrapper<OtherConcreteNongeneric>, OtherConcreteNongeneric>();
foo.Start();
bar.Start();
}
}
Thank you very much for your time and help!

c# generic delegate to manage instantiated objects

I'm trying to figure out how to use a generic delegate to manage my instantiated objects in a game engine.
Here is some pseudo-code to demonstrate what I'm trying to do:
public class ObjectManager
{
public delegate void ObjectManagerEvent <T> (T instantiatedObject);
public ObjectManagerEvent <T> onObjectInstantiated;
public void InstantiateObject (Object objToInstantiate)
{
var obj = SomeInternalInstantiateMethod ();
ObjectManagerEvent _onObjectInstantiated = onObjectInstantiated;
if (_onObjectInstantiated != null)
{
_onObjectInstantiated (obj);
}
}
}
public class Shape : EBehaviour {}
public class Animal : EBehaviour {}
public class DemoShape
{
private void Init ()
{
ObjectManager.onObjectInstantiated += OnObjectInstaniated;
}
public void OnObjectInstaniated (Shape shape)
{
// do something with shape
}
}
public class DemoAnimal
{
private void Init ()
{
ObjectManager.onObjectInstantiated += OnObjectInstaniated;
}
public void OnObjectInstaniated (Animal animal)
{
// do something with animal
}
}
I know that public ObjectManagerEvent <T> onObjectInstantiated (); would throw an error, but I'm just kind of lost on how to achieve what I want.
Any pointers?
First, your delegate syntax is very C# 1.0.
Option 1
You can't do this in a particularly simple and elegant way because in C# you cannot use an open generic type to declare a generic event. The closest that we can do is create a dictionary of objects, each of which has an event, and we can use generic methods to access this dictionary.
I also assume you intend InstantiateObject to create and return a new instance. Here I also assume everything is a class with a parameterless constructor.
public static class ObjectManager
{
public class TypeEvent<T>
{
// Our event handler will accept a parameter of type T and return void
public event Action<T> OnObjectInstantiated;
public void RaiseObjectInstantiated(T obj)
{
OnObjectInstantiated?.Invoke(obj);
}
}
private static Dictionary<Type, object> _typeMap = new Dictionary<Type, object>();
public static TypeEvent<T> ForType<T>() where T: class, new()
{
Type t = typeof(T);
if (!_typeMap.ContainsKey(t))
{
_typeMap[t] = new TypeEvent<T>();
}
return _typeMap[t] as TypeEvent<T>;
}
public static T InstantiateObject<T>() where T: class, new()
{
T obj = new T();
ForType<T>().RaiseObjectInstantiated(obj);
return obj;
}
}
You could use it like so:
ObjectManager.ForType<Foo>().OnObjectInstantiated += fooInstantiated;
Foo f = ObjectManager.InstantiateObject<Foo>();
Option 2
If you are okay with making ObjectManager itself a static generic class, you could greatly simplify this. Note this means you no longer have just one ObjectManager class - ObjectManager<Foo> and ObjectManager<Bar> are now different classes with different variables. If that's acceptable to you, this makes things a lot cleaner for the small bit you've told us that ObjectManager needs to do:
public static class ObjectManager<T> where T : class, new()
{
// Our event handler will accept a parameter of type T and return void
public static event Action<T> OnObjectInstantiated;
public static T InstantiateObject()
{
T obj = new T();
OnObjectInstantiated?.Invoke(obj);
return obj;
}
}

Array of inherited from generic types

Code to demonstrate the problem:
static void Main(string[] args)
{
var a = new A();
var b = new B();
Base<>[] all = new Base<>[] { a, b }; // doesn't work
}
class Base<T>
{
public string Caption { get { return typeof(T).ToString(); } }
}
class A : Base<A> { }
class B : Base<B> { }
Perhaps I went the wrong direction. Idea was to move Caption into base class (Base become generic). Non-generic version works without problems:
var all = new Base[] { a, b }; // no problems for as long as Base is not generic
There's no Type<?> in C# - you always have to specify a concrete generic type.
The only way around this is to make Base<T> inherit a non-generic base-class, or implement a non-generic interface. You could then use that as the type of the array.
EDIT:
In your case this is extremely simple, since the part of the interface you want doesn't include the generic type argument. So you can simply do either:
public abstract class Superbase
{
public abstract string Caption { get; }
}
public class Base<T>: Superbase
{
public override string Caption { get { return typeof(T).Name; } }
}
Or, using an interface:
public interface IBase
{
string Caption { get; }
}
public class Base<T>: IBase
{
public string Caption { get { return typeof(T).Name; } }
}
Your array would then be Superbase[] or IBase[], respectivelly. In both cases, you can see that I'm not actually providing an implementation - both the declarations are "abstract", in a sense.
In general, I'm trying to keep the non-generic stuff in a non-generic base class, rather than stuffing it in the derived generic classes. It just feels more clean :)
based on #Luaan ideea, here is an implementation:
class Program
{
static void Main(string[] args)
{
var a = new A();
var b = new B();
var arr = new Base[] { a, b};
foreach (var obj in arr)
Console.WriteLine(obj.Caption);
Console.ReadKey();
}
}
public class Base<T> : Base
{
public override string Caption
{
get { return typeof (T).ToString(); }
}
}
public class A : Base<A> { }
public class B : Base<B> { }
public abstract class Base
{
public abstract string Caption { get; }
}
Instead of trying to use inheritance (which will lead to more problems down the line), use an extension method instead:
public interface IClassAORClassB {}
class A : IClassAORClassB { }
class B : IClassAORClassB { }
public static class Captions
{
public static string Caption<T>(this T obj) where T : IClassAORClassB
{
return obj.GetType().ToString();
}
}
static void Main(string[] args)
{
var a = new A();
var b = new B();
var all = new IClassAORClassB[] { a, b }; // works just fine
Console.WriteLine(all[0].Caption()); // prints A
Console.WriteLine(all[1].Caption()); // prints B
}

How to hide a member function when the base class uses generics

I have the following classes, and when I call CreateQuerySettings on the BaseScriptConfigurationList, it returns the new QuerySettings from ConfigurationList, rather than the HierarchicalQuerySettings value in BaseScriptConfigurationList:
public abstract class ConfigurationList<TConfigurationObject, TPropertyEnum>
{
public QuerySettings<TConfigurationObject, TPropertyEnum> CreateQuerySettings()
{
return new QuerySettings<TConfigurationObject, TPropertyEnum>();
}
}
public class BaseScriptConfigurationList : EditableConfigurationList<BaseScriptConfiguration, BaseScriptConfiguration.Property>
{
public BaseScriptConfigurationList(ConfigurationManager configurationManager)
: base(configurationManager, InternalAdminObjectType.BaseScript)
{
_BaseScriptPageListWatcher = new ConfigurationList<BaseScriptPageConfiguration, BaseScriptPageConfiguration.Property>.
ConfigurationWatcher(null);
_ConfigurationWatcher.ChildWatchers.Add(_BaseScriptPageListWatcher);
}
public new QuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property> CreateQuerySettings()
{
return new HierarchicalQuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property, BaseScriptQueryChildrenSettings>();
}
}
Edit: I make the call from another class where TConfigurationObjectList is BaseScriptConfigurationList. I've added the constructor to the code above so you can see what it's doing. Please note that EditableConfigurationList inherits from ConfigurationList.
TConfigurationObjectList cl = (TConfigurationObjectList)typeof(TConfigurationObjectList).GetConstructor(new Type[] { typeof(ConfigurationManager) }).Invoke(new object[] { Manager.ConfigurationManager });
var querySettings = cl.CreateQuerySettings();
When I make this call, it goes into the ConfigurationList.CreateQuerySettings method.
How can I hide the CreateQuerySettings method, so that when I call it from the BaseScriptConfigurationList class, I get a HierarchicalQuerySettings object?
The new modifier can be beasty. Note that you are hiding and not overriding in your example. You are not showing that part of the code, but I assume you have this situation:
class Base
{
public static void BaseMethod() { Console.WriteLine("BASE!"); }
}
class Derived : Base
{
// Hides Base.BaseMethod()
new public static void BaseMethod() { Console.WriteLine("DERIVED!"); }
}
Base a = new Base();
a.BaseMethod(); // -> "BASE!"
Base b = new Derived();
b.BaseMethod(); // -> "BASE!"
Derived b = new Derived();
b.BaseMethod(); // -> "DERIVED!"
In BaseScriptConfigurationList.CreateQuerySettings()
you're return type is QuerySettings<T,T> so you will always get that type as a return value, but you are returning a HierarchicalQuerySettings. You can one, change the return type of CreateQuerySettings() to HierarchicalQuerySettings or two, cast the object to its child type "HierarchicalQuerySettings". If you really want to hide it, you can do this:
public class newclass : BaseScriptConfigurationList
{
public new HierarchicalQuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property> CreateQuerySettings()
{
return (HierarchicalQuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property>)base.CreateQuerySettings();
}
}
But that doesn't really seem efficient and i advise against it. Like i said, i maybe missing some other requirement, but based on the info that you gave..
Basically, what I'm seeing (and making assumptions) that TConfigurationObjectList Inhertis from ConfigurationList somewhere along the lines, so on and so forth, all the way up to EditableConfigurationList. since you are dynamically creating an instance of the class TConfigurationObjectList, and calling the method from that point, you will be calling the base ConfigurationList member CreateQuerySettings. You do not have access to the new CreateQuerySettings. If you are creating the class BaseScriptConfigurationList instance at this point, cast the object ((BaseScriptConfigurationList)cl).CreateQuerySettings(). That being said. if you do not know what you have at runtime:
var obj = typeof(TConfigurationObjectList).GetConstructor(new Type[] { typeof(ConfigurationManager) }).Invoke(new object[] { Manager.ConfigurationManager });
var cl = (obj as BaseScriptConfigurationList) ?? (TConfigurationObjectList)obj;
// or do something else
var querySettings = cl.CreateQuerySettings();
Note i am assuming your architecture is roughly set up like this:
public abstract class ConfigurationList<TConfigurationObject, TPropertyEnum>
{
public QuerySettings<TConfigurationObject, TPropertyEnum> CreateQuerySettings()
{
return new QuerySettings<TConfigurationObject, TPropertyEnum>();
}
}
public class TConfigurationObjectList : ConfigurationList<BaseScriptConfiguration, BaseScriptConfiguration.Property>
{
}
public class EditableConfigurationList<T, T1> : TConfigurationObjectList
{
protected EditableConfigurationList(ConfigurationManager configurationManager, object baseScript)
{
throw new NotImplementedException();
}
}
public class BaseScriptConfigurationList : EditableConfigurationList<BaseScriptConfiguration, BaseScriptConfiguration.Property>
{
public BaseScriptConfigurationList(ConfigurationManager configurationManager)
: base(configurationManager, InternalAdminObjectType.BaseScript)
{
}
public new QuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property> CreateQuerySettings()
{
return new HierarchicalQuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property, BaseScriptQueryChildrenSettings>();
}
}
public class QuerySettings<T, T1>
{
}
public class HierarchicalQuerySettings<T, T1, T2> : QuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property>
{
}
public class BaseScriptQueryChildrenSettings
{
}
public class BaseScriptPageConfiguration
{
public class Property
{
}
}
public class InternalAdminObjectType
{
public static object BaseScript { get; set; }
}
public class ConfigurationManager
{
}
public class BaseScriptConfiguration
{
public class Property
{
}
}
Create a base interface for the ConfigurationList class (say IConfigurationList) and use this interface as the data type for the variable cl instead of TConfigurationList.

Generic parameter base type: "There is no implicit reference conversion from B to A"

[Serializable]
public abstract class A
{
public A()
{
}
}
[Serializable]
public class B : A
{
public B() : base()
{
}
}
In an extension:
public static T NextRecord<T>(this SqlDataReader reader) where T : A, new()
{
// Do work
}
I call this extension like so:
B b = reader.NextRecord<B>();
Yet, I get this exception: "There is no implicit reference conversion from 'B' to 'A'."
What am I doing wrong?
Thanks.
Edit
public static T NextRecord<T>(this SqlDataReader reader) where T : A, new()
{
// Make sure we have been given a correct Type
if (!typeof(T).BaseType.Equals(typeof(A)))
{
throw new Exception("Supplied Type is not derived from Type A");
}
if (reader.IsNull())
{
throw new ArgumentNullException("reader is null");
}
if (reader.HasRows)
{
if (reader.Read())
{
// Instance a object of the type, passing it the SqlDataReader so that it can populate itself
return Activator.CreateInstance(typeof(T), new object[] { reader }) as T;
}
}
return null;
}
Here's the code for the extension
Still having problems with this, although i sort of fixed the problem by changing where T : A to where T : class.
I have a 2nd problem:
public class SomeOtherClass
{
public static void Test(SomeClassBase obj)
{
}
}
public abstract class SomeClassBase
{
public SomeClassBase()
{
SomeOtherClass.Test(this);
}
}
public class SomeClass : SomeClassBase
{
public SomeClass() : base()
{
}
}
I Get this exception:
"The best overloaded method match for 'Namespace.SomeOtherClass.Test(Namespace.SomeClass)' has some invalid arguments"
However, removing the mothod call from the ctor and calling it outside will work, like so:
SomeClass s = new SomeClass();
SomeOtherClass.Test(s);
Even changing the method to:
public static void Test(SomeClass obj)
{
}
will fail :(
and if i move the method call to the derived class ctor...

Categories