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.
Related
Good day, all.
May I ask you logic why this is not possible?
I'm studying interface & generic now and I think this is possible because Group implements both iPoppable & iPushable. But converting iPoppable to Group is not possible, compiler complains. I'd like to know the logic why this is not possible.
interface iPoppable<out T>{T Pop();}
interface iPushable<in T>{void Push(T ag_t);}
class Program
{
static void Main()
{
iPoppable<Lion> lions = new Group<Lion>();
iPoppable<Animal> animals = lions; //Possible
Group<Lion> lions2 = lions; //Not possible
}
}
class Animal{}
class Lion:Animal{}
class Group<T>:iPoppable<T>, iPushable<T>
{
public void Push(T ag_t){}
public T Pop(){return something;}
}
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; }
}
I have this simple class
public class Program
{
private static void Main(string[] args)
{
ClassB<ClassA> objA = new ClassB<ClassA>();
ClassB<ITestA<MyDTO>> objB = new ClassB<ClassA>();
}
}
public class ClassB<T>
{
///some code here
}
public interface ITestA<T>
{
///some code here
}
public class MyDTO
{
///some code here
}
public class ClassA : ITestA<MyDTO>
{
///some code
}
This line of code
ClassB<ITestA<MyDTO>> objB = new ClassB<ClassA>();
is giving compilation error
Cannot implicitly convert type 'ClassB<ClassA>' to 'ClassB<ITestA<MyDTO>>
Since ClassA implements ITestA, I don't know why would this give a compilation error. Please help me understand what am I doing wrong.
Thanks,
Esen
It's due to a rather complex feature of generics called variance.
Classes are invariant, which means that if you declare ClassB<T>, then when creating an instance:
ClassB<T1> obj = new ClassB<T2>
Then T1 has to be exactly the same class as T2.
You can use interfaces to get around this, eg change your code to the following and it'll compile:
...
public class Program
{
private static void Main(string[] args)
{
ClassB<ClassA> objA = new ClassB<ClassA>();
IClassB<ITestA<MyDTO>> objB = new ClassB<ClassA>();
}
}
public interface IClassB<out T> // <- note the out before T
{
//some code here
}
public class ClassB<T> : IClassB<T>
{
//some code here
}
...
In this case, IClassB is declared as covariant, which means that it can handle being given a derived class of T, rather than needing T itself. There are risks around using covariance (and contravariance) though, which is why generic classes are invariant by default.
I have a covariant/contravariant issue with generics.
Here's a code of what I'm trying to do :
in framework DLL:
namespace framework {
public interface IBase
{
//Some code...
}
public abstract class AOverClass<T> : IBase where T : IBase
{
public AOverClass(T pObject)
{
//Some code
}
//Some code...
}
public class workerClass
{
public Dictionary<Type, Type> ObjDictionary;
public AOverClass<IBase> GetOverObj(IBase initObj){
// Get the over Obj type from a dictionary that was loaded previously
// the dictionary contains KVP of <Type, Type> then using Reflection
// to initialise a new OverObj
Type t = Dictionary[initObj.GetType()];
Type[] _typesParamsConstructor = new Type[1];
_typesParamsConstructor[0] = initObj.GetType();
Object[] _valParamsConstructor = new Object[1];
_valParamsConstructor [0] = initObj;
// BIG ISSUE HERE
return (AOverClass<IBase>)t.GetConstructor(_typesParamsConstructor).Invoke(_valParamsConstructor);
}
}
}
in application that references the framework DLL:
namespace myApp {
public class Param : framework.IBase, myApp.IOtherNeeded
{
//Some code
}
public class OverParam : framework.AOverClass<Param>
{
public OverParam(Param pObject) :base(pObject)
{
//Some code...
}
}
public class App
{
private framework.workerClass _wc;
public void Init()
{
_wc = new framework.workerClass();
_wc.ObjDictionary.Add(typeof(Param), typeof(OverParam));
}
public void Run()
{
_wc.GetOverObj(new Param());
}
}
}
The workerClass throws an Exception that it can't cast OverParam to AOverClass<IBase>.
How can I work round this?
Foot note :
There are no initialisation issues (i.e. the dictionary), I'm just no writting all the code that is not relevent to the issue.
An instance of OverParam is an AOverClass<Param> by definition.
But you are trying to cast that instance of OverParam to AOverClass<IBase>. That fails.
Now just because any
Param "is" IBase
can we or can we not conclude that every
AOverClass<Param> "is" AOverClass<IBase>
?
The answer is: That works precisely if AOverClass<T> is covariant in T. The only types that can be covariant (marked with out before the type parameter T) in the current version of C# and .NET, are interface types and delegate types. But AOverClass<T> is a class type.
Also, when we look at your class:
public abstract class AOverClass<T> : IBase where T : IBase
{
public AOverClass(T pObject) // an "in" parameter of type T here! :-(
{
//Some code
}
//Some code...
}
we see that you use T contravariantly. So even if the language allowed class types to be covariant, it wouldn't apply in your case. This indicates that there is something fundamentally wrong with your assumptions.
I have a generic class who holds a member whose type is an argument type.
I want to be able to access this member only by one of the interface it implements.
The reason I want to access the member only via this interface, instead of enumerating all the concrete types it could be, is because there are a great number of those types.
So concretely I want to find an equivalent of line 61 in that code (it is a compilation error):
using System;
using System.Linq;
/* Interfaces */
public interface IArgumentClass
{
void IArgumentClassMethod();
}
public interface ISpecialArgumentClass
{
void ISpecialArgumentClassMethod();
}
public interface IContainerClass
{
void IContainerClassClassMethod();
}
/* Argument types */
public class ArgumentClass0 : IArgumentClass
{
public void IArgumentClassMethod(){}
}
public class SpecialArgumentClass0 : IArgumentClass, ISpecialArgumentClass
{
public void IArgumentClassMethod(){}
public void ISpecialArgumentClassMethod(){}
}
public class SpecialArgumentClass1 : IArgumentClass, ISpecialArgumentClass
{
public void IArgumentClassMethod() { }
public void ISpecialArgumentClassMethod() { }
}
/* Container types */
public class GenericContainer<T> : IContainerClass
where T : IArgumentClass, new()
{
public T t = new T();
public void IContainerClassClassMethod() { }
}
public class NonGenericContainer : IContainerClass
{
public void IContainerClassClassMethod(){}
}
/* main program */
public class Test
{
public static void Main()
{
// Instantiate
IContainerClass[] containers =
{
new GenericContainer<ArgumentClass0>(),
new GenericContainer<SpecialArgumentClass0>(),
new GenericContainer<SpecialArgumentClass1>(),
new NonGenericContainer()
};
// We want to call IContainerClassClassMethod methods on all instances:
foreach (IContainerClass container in containers)
container.IContainerClassClassMethod();
// We want to call ISpecialArgumentClassMethod on instances where it's possible:
foreach (IContainerClass container in containers)
{
if (container.GetType().IsGenericType && container.GetType().GetGenericTypeDefinition() == typeof(GenericContainer<>))
{
foreach (Type typeArgument in container.GetType().GetGenericArguments())
{
if (typeArgument.GetInterfaces().Contains(typeof(ISpecialArgumentClass)))
{
// Next line is a compilation error. How can I get a similar result?
GenericContainer<ISpecialArgumentClass> mySpecializedClassWithSpecialArgument = container as GenericContainer<ISpecialArgumentClass>;
mySpecializedClassWithSpecialArgument.t.ISpecialArgumentClassMethod();
}
}
}
}
}
}
Note: You can fork and edit the code here.
You get the compilation error because ISpecialArgumentClass is not of type IArgumentClass, but your GenericClass requires exactly this.
To solve this, you could introduce an empty interface which serves as base for both argument classes:
First, modify your interface declaration like this:
public interface IArgumentClassBase
{
}
public interface IArgumentClass : IArgumentClassBase
{
String GetNormalString();
}
public interface ISpecialArgumentClass : IArgumentClassBase
{
String GetSpecialString();
}
... then modify your generic class declaration like so:
public class GenericClass<T> : IContainerClass
where T : IArgumentClassBase, new()
Then the rest of your code should work...
A really simple solution is to just cast it to dynamic - you know it has a t field, so this should be safe to do.
if (typeArgument.GetInterfaces().Contains(typeof(ISpecialArgumentClass)))
{
dynamic mySpecializedClassWithSpecialArgument =
mySpecializedClass as dynamic;
ISpecialArgumentClass specialArgumentClass = mySpecializedClassWithSpecialArgument.t;
Console.WriteLine(specialArgumentClass.GetSpecialString());
}
Note
I tried to edit it in ideone, but it would not compile. I suspect it's targeting an older version of .NET - dynamic was introduced in .NET 4 (VS 2010). However, I've tested the code in 2013 and it works.
I've got an interface with some generic methods, and I wanted to implement a method with overloads to either accept an instance of a class, or its PK value (which is either an int or GUID but does vary).
I added to methods similar to these examples:
void DoSomething<TKey>(TKey key) where TKey: struct;
void DoSomething<TModel>(TModel model) where TModel : class;
The 'DoSomething' method name on the second of these is highlighted, and the error is
Type 'ISomeStuff' already defines a member called 'DoSomething' with
the same parameter types.
I'm surprised by this as I've clearly defined by parameters to be of different type: one is a class and the other a struct.
Why isn't this sufficient to make the signatures different?
Is possible to do it, you need create something like enable_if from C++
public class ClassTag<V> where V : class { }
public class StructTag<V> where V : struct { }
public void Func<V>(V v, ClassTag<V> dummy = null) where V : class
{
Console.Writeln("class");
}
public void Func<V>(V v, StructTag<V> dummy = null) where V : struct
{
Console.Writeln("struct");
}
public void Func<V>(V? v, StructTag<V> dummy = null) where V : struct
{
Console.Writeln("struct?");
}
static void Main()
{
Func("A");
Func(5);
Func((int?)5);
}
It can be expanded to use any disjoint where to distinguish between overloads.
Only drawback is that It cant be used inside another generic method:
public static void Z1<T>(T t) // where T : class
{
Func(t); //error there
}
public static void Z2<T>(T t) where T : class
{
Func(t); //ok
}
edit
But there is possibility of use dynamic in that case to work around this limitation:
public static void Z1<T>(T t)
{
Func((dynamic)t); //if `T == int` it will call "struct" version
}
Only drawback is run time cost similar to call to Dictionary<,> index.
Jon Skeet has an answer to everything: click me
quote:
the declarations only differ in generic constraints, and constraints
aren't part of the signature
If one wishes to invoke a member generically regardless of whether it has a class constraint or a struct constraint, and have it invoke a method with a suitable constraint, one may define an interface IThingUser<T> to act upon any type T, along with one a class which implements it for value types and another which implements it for class types. Have a static class ThingUsers<T> with a static field TheUser of type IThingUser<T>, and have it populate that field with an instance of one of the above classes, and then ThingUsers<T>.theUser will be able to act upon any sort of T.
public static class GenTest93
{
public interface IThingUser<T> { void ActOnThing(T it); }
class StructUser<T> : IThingUser<T>, IThingUser<Nullable<T>> where T : struct
{
void IThingUser<T>.ActOnThing(T it) { System.Diagnostics.Debug.Print("Struct {0}", typeof(T)); }
void IThingUser<Nullable<T>>.ActOnThing(T? it) { System.Diagnostics.Debug.Print("Struct? {0}", typeof(T)); }
}
class ClassUser<T> : IThingUser<T> where T : class
{
void IThingUser<T>.ActOnThing(T it) { System.Diagnostics.Debug.Print("Class {0}", typeof(T)); }
}
static class ThingUsers<T>
{
class DefaultUser : IThingUser<T>
{
public void ActOnThing(T it)
{
Type t = typeof(T);
if (t.IsClass)
t = typeof(ClassUser<>).MakeGenericType(typeof(T));
else
{
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>))
t = t.GetGenericArguments()[0];
t = typeof(StructUser<>).MakeGenericType(t);
}
TheUser = (IThingUser<T>)Activator.CreateInstance(t);
TheUser.ActOnThing(it);
}
}
static IThingUser<T> TheUser = new DefaultUser();
public static void ActOnThing(T it) {TheUser.ActOnThing(it);}
}
public static void ActOnThing<T>(T it) { ThingUsers<T>.ActOnThing(it); }
public static void Test()
{
int? foo = 3;
ActOnThing(foo);
ActOnThing(5);
ActOnThing("George");
}
}
It's necessary to use Reflection to create an instance of StructUser<T> or ClassUser<T> if the compiler doesn't know that T satisfies the necessary constraint, but it's not too hard. After the first time ActOnThing<T>() is used for a particular T, ThingUsers<T>.TheUser will be set to an instance which can be used directly for any future calls toActOnThing(), so performance should be very good.
Note that if given a Nullable<T>, the method creates a StructUser<T> and casts it to IThingUser<Nullable<T>>, rather than trying to create a sometype<Nullable<T>>, since nullable types themselves don't satisfy any constraint.
If you don't need generic parameters and just want to differentiate between these cases at compile time, you can use following code.
void Foo(object a) { } // reference type
void Foo<T>(T? a) where T : struct { } // nullable
void Foo(ValueType a) { } // value type