Specify a C# Generic Constraint to be of ClassA OR ClassB? - c#

Is there a way to specify that a generic type be of one type or another type?
public class SoftDrink<T>
where T : TypeOne or TypeTwo
{ }

Nope because that wouldn't make any sense. If you try to access a member function of "T", how can the compiler tell which base class to try and resolve members of?
You could try creating a common interface for TypeOne and TypeTwo and having the constraint specify that interface? What are you trying to accomplish, exactly?

You can't. The best you can do is use a common base class for the two, or a common interface implemented by both as a single type constraint.

No, however, you could constraint the class on a common base or interface.
In the CLR itself, the constraints (except for special ones, like the new-constraint) are stored rather simply: just a list of type identifiers, basically. So there's really no "room" for logical operations like you've specified... not without a revision to the CLR specification, or perhaps some really clever and ugly language-level tricks.

You can extract the common functionality of TypeOne and TypeTwo into a base class or interface, and use that base class or interface as the constraint. Otherwise, how would the compiler know what it could do with T?
public interface IBelch
{
void Belch();
}
public class Coke : IBelch
{
public void Belch()
{
}
}
public class GatorAde : IBelch
{
public void Belch()
{
}
}
public class SoftDrink<T>
where T : IBelch
{
public void DrinkFast(T drink)
{
drink.Belch();
}
}

You could always do a runtime check against the type:
class TypeOne
{
int _integer = 0;
}
class TypeTwo
{
int _integer = 3;
}
class TypeThree
{
}
class SoftDrink<T>
{
public SoftDrink()
{
if (typeof(T) != typeof(TypeOne) && typeof(T) != typeof(TypeTwo))
{
throw (new Exception("Sorry, but T must be TypeOne or TypeTwo"));
}
}
}
//this works:
SoftDrink<TypeOne> t1 = new SoftDrink<TypeThree>();
//throws an exception:
SoftDrink<TypeThree> t3 = new SoftDrink<TypeThree>();

You could have TypeOne and TypeTwo inherit an interface and then do the following.
public class SoftDrink<T>
where T: IMyType
{}

Related

How to restrict generic function to accept only some type of classes

I'm trying to do the following:
public class A
{
}
public class B
{
}
Somewhere along the project I want to have this:
public class C
{
public T func<T>(T obj) [where T can be either of class A or class B]
{
obj.x = 100;
return obj;
}
}
I've been trying:
public T func<T>(T obj) where T: A, B
but this gives me:
The type class constraint 'B' must come before any other constraint.
Can someone explain me how to make func accept only class A or class B?
Exactly as it's described in the question, this job is better handled by overload resolution:
public class C
{
public A func(A obj)
{
obj.x = 100;
return obj;
}
public B func(B obj)
{
obj.x = 100;
return obj;
}
}
But I understand that A and B may be placeholders for any number of types, and it could get tedious to account for them all. In that case, you'll need a common interface that's supported by each of your classes:
interface IBase
{
int x;
}
public class C
{
public IBase func(IBase obj)
{
obj.x = 100;
return obj;
}
}
Note that at this point we still have no need of generics. Additionally, you may need to support a number of types that won't all fit together under a common interface. In this case, still build the interface and put as many types with that interface as possible. If needed, build another interface for a few more types ... and so on... and then between interfaces and specific types you can handle things with overload resolution.
You need some kind of common base for both the classes, either have them implement the same interface as the below code or have them inherit from same class. You can not have a generic constrained to 2 types.
public interface IFooBar
{
void DoThis();
}
public class Foo : IFooBar
{
public void DoThis()
{
//Do something
}
}
public class Bar : IFooBar
{
public void DoThis()
{
//Do something
}
}
public class C
{
public T func<T>(T obj) where T : IFooBar
{
obj.DoThis();
return obj;
}
}
The generics classes are just like any other class, you can't (and shouldn't) have multiple inheritance of classes, you can inherit one class and multiple interfaces.
in your case you should apply an interface on both classes and restrict the generics on that interface.
you can see some documentation in:
Constraints on Type Parameters (C# Programming Guide)
interface IMarkerInterface{} // there is a such pattern called marker
// interface . No need to have methods if there
// is no need for, A and B can just implement it
public class A: IMarkerInterface
{
}
public class B: IMarkerInterface
{
}
public class C
{
public T func<T>(T obj).Where T:IMarkerInterface
{
obj.x = 100;
return obj;
}
}
public T func<T>(T obj) where T: A, B this means T should extend both A and B , but multiple inheritance is not valid in C# ,so it won't work.
You could do one of the following though :
you could make A and B have a common parent via an interface or an abstract class , but that would be code modification.
since both A and B have a default no-arg constructor you could use where T: new().
Also, you can not do obj.x = 100; as there is no way to guarantee thatT will have a instance variable x.

C# Generics, interfaces and inheritance

I've two interfaces:
public interface IAmA
{
}
public interface IAmB<T> where T : IAmA
{
}
And two classes implementing these interfaces like this:
public class ClassA : IAmA
{
}
public class ClassB : IAmB<ClassA>
{
}
When trying to use these classes as shown:
public class Foo
{
public void Bar()
{
var list = new List<IAmB<IAmA>>();
list.Add(new ClassB());
}
}
I get this compiler error:
cannot convert from 'ClassB' to 'IAmB<IAmA>'
I know I can make the compiler happy using:
public class ClassB : IAmB<IAmA>
{
}
But I need to be able to be the Type parameter for IAmB<> in ClassB an implementation of IAmA.
The quick answer is that you can do what you ask by declaring the type parameter of IAmB<T> as covariant, only if the type is used as a return type:
public interface IAmB<out T> where T : IAmA
{
T SomeMethod(string someparam);
}
out T means that you can use a more specific type than then one specified in the constraints.
You won't be able to use T as a parameter. The following won't compile:
public interface IAmB<out T> where T : IAmA
{
void SomeMethod(T someparam);
}
From the documentation
You can use a covariant type parameter as the return value of a method that belongs to an interface, or as the return type of a delegate. You cannot use a covariant type parameter as a generic type constraint for interface methods.
This isn't a compiler quirk.
Assuming you could declare a covariant method parameter, your list would end up containing some objects that couldn't handle an IAmB<IAmA> parameter - they would expect an input of ClassA or more specific. Your code would compile but fail at runtime.
Which begs the question - why do you want to use IAmB<ClassA> ?
You should think about before using this though, as there may be other, more suitable ways to address your actual problem. It's unusual to use a generic interface implementing a concrete type but trying to use it as if it were implementing another interface.
You can check the MSDN documentation's section on Covariance and Contravariance as well as Eric Lippert's an Jon Skeet's answers to this SO question: Difference between Covariance and Contravariance
Fast answer : make the generic type covariant (see msdn) in your interface
public interface IAmB<out T> where T : IAmA
{
}
this will resolve the compiler problem.
But this won't answer the why asked by Panagiotis Kanavos !
The trick is making the type constraint T on IAmB<T> covariant, with the out keyword:
public interface IAmB<out T> where T : IAmA
{
}
This allows you to use a more specific type than originally specified, in this case allowing you to assign an IAmB<ClassA> to a variable of type IAmB<IAmA>.
For more information, see the documentation.
I just tell why this error reported.
if your IAmB has a method
public interface IAmB<T> where T : IAmA
{
void foo(T p);
}
public class ClassB : IAmB<ClassA>
{
void foo(ClassA p)
{
p.someIntField++;
}
}
and we have another class
public class ClassC : IAmB<ClassA2>
{
void foo(ClassA2 p)
{
p.someOtherIntField++;
}
}
and we assume List<IAmB<IAmA>>.Add(T p) implement like this
IAmA mParam = xxxx;
void Add(IAmB<IAmA>> p){
p.foo(mParam);
}
thinking all compile OK. you pass a ClassB instance to List.Add, it becomes
void Add(IAmB<IAmA>> p){
//p is ClassB now
p.foo(mParam);//COMPILER CAN NOT MAKE SURE mParam fit ClassB.foo
}
It can be solved using Contravariance and Covariance.
public interface IAmA
{
}
**public interface IAmB<out T> where T : IAmA
{
}**
public class ClassA : IAmA
{
}
public class ClassB : IAmB<ClassA>
{
}
public class Foo
{
public void Bar()
{
var list = new List<IAmB<IAmA>>();
**list.Add(new ClassB());**
}
}
Now you don't get compiler error. Compiler is happy.

Argument type matches this class

Consider the following:
public interface ITree<X>
{
...
ITree<X> Union(ITree<X> other);
...
}
The idea is that I'm going to implement several types of tree. However, the Union() method only works if you try to union two trees of the same type. The type signature above does not enforce this restriction, however.
So, my question is: How can I write a type signature for Union() such that the other argument must have the same type as this?
(Obviously I can do a dynamic run-time test and throw an exception if the types don't match. But I would much, much rather to check this at compile-time if it can be done...)
There isn't a particularly clean way of expressing this, this is a consequence of using interfaces, since there's no way to know the implementing type of ITree<X>. The best method is probably to create another class/interface which constrains the concrete tree type and does the operation(s) you require:
public interface ITreeUnion<T, X> where T : ITree<X>
{
T Union(T left, T right);
}
you'll then have to pass instances of this interface type to where you need to carry out the required operation.
If you really require Union to go on the interface you can use a recurring template:
public interface ITree<T, X> where T : ITree<T, X>
{
T Union(T other);
}
public class RedBlackTree<T> : ITree<RedBlackTree<T>, T>
{
public RedBlackTree<T> Union(RedBlackTree<T> other)
{
}
}
According to your requirment, you would need a generic declaration of Union().
interface
public partial interface ITree<X> {
T Union<T>(T other) where T: ITree<X>;
}
sample classes
public partial class TreeOfObject: ITree<object> {
public T Union<T>(T other) where T: ITree<object> {
return default(T); // sample only; shuold be implemented yourself
}
}
public partial class TreeOfInt: ITree<int> {
public T Union<T>(T other) where T: ITree<int> {
return default(T); // sample only; shuold be implemented yourself
}
}
test
public static partial class TestClass {
public static void TestMethod() {
var x=new TreeOfObject();
var y=new TreeOfInt();
var xx=x.Union(x);
var yy=y.Union(y);
var xy=x.Union(y); // won't compile
var yx=y.Union(x); // won't compile
}
}
Why do you need the interface then? Simply implement a Replace method on each implementation of a tree:
public class RedBlackTree<T> {
public RedBlackTree<T> Union(RedBlackTree<T> other) { ... }
}
public class SplayTree<T> {
public SplayTree<T> Union(SplayTree<T> other) { ... }
}
Since you're looking for compile-time safety when dealing with each implementation of ITree, I would argue you just need to deal with the concrete types. Of course, you could have an ITree<T> with other methods on it if you require.
Somehow, the following actually compiles:
public interface ITree<TSelf, TItem> where TSelf : ITree<TSelf, TItem>
{
TSelf Union(TSelf other);
// ...
}
public class AvlTree<TItem> : ITree<AvlTree<TItem>, TItem> {
public AvlTree<TItem> Union(AvlTree<TItem> other) {
return other;
}
}
Of course it's not particularly useful, since then you'd have to declare variables as ITree<AvlTree>, at which point you might as well not use the interface. With C# generics, the values of generic type parameters have to be known at some point to reify the generic type.

c# - cast generic class to its base non-generic class

I have following classes:
public abstract class CustomerBase
{
public long CustomerNumber { get; set; }
public string Name { get; set; }
}
public abstract class CustomerWithChildern<T> : CustomerBase
where T: CustomerBase
{
public IList<T> Childern { get; private set; }
public CustomerWithChildern()
{
Childern = new List<T>();
}
}
public class SalesOffice : CustomerWithChildern<NationalNegotiation>
{
}
The SalesOffice is just one of few classes which represent different levels of customer hierarchy. Now I need to walk through this hierarchy from some point (CustomerBase). I can't figure out how to implement without using reflection. I'd like to implement something like:
public void WalkHierarchy(CustomerBase start)
{
Print(start.CustomerNumber);
if (start is CustomerWithChildern<>)
{
foreach(ch in start.Childern)
{
WalkHierarchy(ch);
}
}
}
Is there any chance I could get something like this working?
The solution based on suggested has-childern interface I implemented:
public interface ICustomerWithChildern
{
IEnumerable ChildernEnum { get; }
}
public abstract class CustomerWithChildern<T> : CustomerBase, ICustomerWithChildern
where T: CustomerBase
{
public IEnumerable ChildernEnum { get { return Childern; } }
public IList<T> Childern { get; private set; }
public CustomerWithChildern()
{
Childern = new List<T>();
}
}
public void WalkHierarchy(CustomerBase start)
{
var x = start.CustomerNumber;
var c = start as ICustomerWithChildern;
if (c != null)
{
foreach(var ch in c.ChildernEnum)
{
WalkHierarchy((CustomerBase)ch);
}
}
}
You could move the WalkHierarchy method to the base class and make it virtual. The base class implementation would only process the current node. For the CustomerWithChildern<T> class, the override would do an actual walk.
Try this:
if(start.GetType().GetGenericTypeDefinition() == typeof(CustomerWithChildern<>))
I believe that you want to make the lookup for the determination of doing to the walk an interface.
So maybe add an "IWalkable" interface that exposes the information needed to do the walk, then you can create your method checking to see if the passed object implements the interface.
"Is" and "As" only work on fully qualified generic types.
See this MSDN discussion for details including workarounds.
The most common workaround I've seen is to add an interface to the mix that your CustomerWithChildren could implement, and check for that interface.
I think everyone hits this "issue" when first working with generic classes.
Your first problem is hinted at in your question phrasing: an open generic type is NOT the base class to a closed one. There is no OO relationship here, at all. The real base class is CustomerBase. An "open" generic type is like a half-completed class; specifying type arguments "closes" it, making it complete.
While you can do:
Type t = typeof(CustomerWithChildern<>)
the condition
typeof(CustomerWithChildern<>).IsAssignableFrom(CustomerWithChildern<Foo>)
will always be False.
-Oisin
Explicitly with that method, no. However you can achieve the same functionality with an interface. In fact, you could just have your generic class implement IEnumerable. It's also worth noting that your class should also have "where T : CustomerBase" in order to ensure type safety.

Generics c#.net

In the below code "where T : WsgTypes.RouteRestriction", can I add multiple classes so that T can be of only those few classes types which I am interested of
public static T GetDetails<T>(string code) where T : WsgTypes.RouteRestriction
{
T details;
if (typeof(T) == typeof(WsgTypes.TicketType))
{
details = TicketTypeDetail.GetDetails(code) as T;
}
else if (typeof(T) == typeof(WsgTypes.RouteRestriction))
{
details = RouteRestrictionDetail.GetDetails(code) as T;
}
else
{
throw new NotSupportedException("");
}
return details;
throw new NotImplementedException();
}
It seems to me that this isn't a proper use of generics. It would be better if TicketType and RouteRestriction implemented some IDetailed.
No, generic type constraints can only specify a single base-class.
You can specify multiple interfaces, but this is "all of", not "any of".
What you ask is possible with overloading, but not with generics.
For inheritance you can have a single class with multiple interfaces.
public static T GetDetails<T>(string code) where T : WsgTypes.RouteRestriction, , IComparable
{
}
Instead you can have an interface and have multiple classes implementing it.
public interface IInterface
{}
public class Class1: IInterface
{}
public class Class2: IInterface
{}
public static T GetDetails<T>(string code) where T:IInterface
{
T instance;
// ...
return instance;
}
I'm afraid you can't. The usual way is to provide a common interface that all classes you're interested in implement. The problem is that, inside the generic body, the compiler expects a generic type parameter to be unambigous.
Well, or you could take an object as parameter and cast it at your will. But... no. Don't.
Note that instead of typeof, you could also use the is and as operators.
See this article...
http://msdn.microsoft.com/en-us/library/d5x73970.aspx
... for more information on constaints. You can add multiple constraints, and you can constrain by some interface or by some base class, but not by a list of arbitrary classes.
Here's an example of multiple constraints (from the above):
class Base { }
class Test<T, U>
where U : struct
where T : Base, new() { }
Did you try separating them like this:
public static T GetDetails<T>(string code) where T : WsgTypes.RouteRestriction, NextClass, AnotherClass, AndSoOn
{
...
}

Categories

Resources