I have the generic types A<T> and B<T> and now I want to construct the type C<T> : A<B<T>> which as you can see is a very specific kind of A<T>.
It tried defining C just like that but I get
The type `A<B<T>>' does not contain a constructor that takes `0' arguments
Just in case I built the constructor
public C () {}
but I still get the error.
Note: This is an abstraction of the problem. Assume that A and B have a constructor with the form
public A/B (T t)
Your generic type declaration is okay, but your parameterless constructor doesn't have a counterpart in A<T>.
You should call the base class' constructor with the parameters it requires.
Something like:
public C() : base("param1", "param2", 3)
{
}
Related
I got two bases classes A and B; B is related to A by composition. Also, both classes have constraints on type parameters (generic classes), and in this case class A is constraining one of its parameter type to B's type. So my question is... when constraining class A parameter's type to B's type, why I should specify the parameter's types for class B as well? I only see that I must provide B parameter types when I would inherit the class B, not when its type it's being used for parameter type constraints in a class.
Check a code snippet that illustrates what I'm explaining above:
public abstract class B<TParameter, TResponse>
where TParameter: FooParameter
where TResponse: FooResponse
{
}
public abstract class A<T, TB>
where T: class
where TB: B /*compilation error because B types must be provided... I must define parameter types for class B*/
{
public TB B {get; set;}
public A()
{
//instantiate B property
}
}
public class FooParameter {}
public class FooResponse {}
When you create a generic type, you're creating a conceptual "template" more than a complete type. It can only become a complete type when you specify the missing pieces in the template: the concrete types.
Type constraints are there so the compiler can add some type-checking. Even if you don't know the exact type that's going to be used, if it inherits from a base, you know what base members are available to your code. If you try to constrain a type parameter with a generic type without type arguments, the compiler doesn't have enough information to understand what methods will actually be available.
As an example, if we replace TB: B with the type TB: List<T>, the compiler would not know what the list is of. Methods like Add(T) and Remove(T) couldn't be compiled against, because T would be unknown.
For type constraints to be valuable, they must constrain the type system in such a way as to add useful information. The only way for that information to be useful is for the type to be concretely specified.
Try:
public abstract class A<T, TB, C, D>
where T : class
where C : FooParameter
where D : FooResponse
where TB : B<C,D> {
public TB B { get; set; }
public A()
{
//instantiate B property
}
}
You need to be able to define all of the types that a generic needs at compile time, which you can as above.
I am new to C# and I am faced with a class with this structure:
public class SimpleGetter<TSubs> : GetterBase<TSubs>, ISubscriptionsSingleGetter<TSubs>
where TSubs : class, ISimpleSubscription, new()
{
UserSubscriptionsResponse<TSubs> ISubscriptionsSingleGetter<TSubs>.Get()
{
return ((ISubscriptionsSingleGetter<TSubs>)this).Get(null);
}
UserSubscriptionsResponse<TSubs> ISubscriptionsSingleGetter<TSubs>.Get(string userId)
{
return GetSubsResponse(userId);
}
}
I need to pass userID to the get() function (if possible), but I am confused on how to do that. I have tried to do some research on this but I do not even know what this way of defining a class is called. I come from objective c where things seem more straight forward.
I do not even know what this way of defining a class is called
This is a generic class.
public class SimpleGetter<TSubs> : GetterBase<TSubs>, ISubscriptionsSingleGetter<TSubs>
where TSubs : class, ISimpleSubscription, new()
which has one generic type parameter TSubs. This class inherits the GetterBase<TSubs> and implements the interface ISubscriptionsSingleGetter<TSubs>. Furthermore, the TSubs must be a reference type and must have a parameterless constructor, which implements the ISimpleSubscription interface.
public class FakeSubs : ISimpleSubscription
{
public FakeSubs()
{
}
// Here you have to implement ISimpleSubscription.
// You could also define any properties, methods etc.
}
// Now you could use your generic class as below:
var simpleGetter = new SimpleGetter<FakeSubs>();
Having created the above instance, you can call the Get method as Tewr, pointed out in his comment:
var response = ((ISubscriptionsSingleGetter<FakeSubs>)simpleGetter).Get(42);
Just to complement Christos' answer and help you understand the syntax a bit better, let's break the class definition term by term.
public - visible to all callers.
class - a reference type (i.e. not a struct).
SimpleGetter<TSubs> - the class name is SimpleGetter, and it is generic with respect to the parameter TSubs.
: GetterBase<TSubs> - it inherits from a base class which is itself generic with respect to the parameter TSubs.
, ISubscriptionsSingleGetter<TSubs> - and it also implements the generic interface ISubscriptionSingleGetter.
where TSubs: - there are some constraints on the type which the generic parameter TSubs must be of.
class - it must itself also be a reference type.
ISimpleSubscription - it must implement this (non-generic) interface.
new() - it must have a public parameterless constructor.
I hope the question is correct, so let's give you an example. Imagine the following generic method:
public abstract class Base : IDisposable
{
public static IEnumerable<T> GetList<T>()
where T : Base
{
// To ensure T inherits from Base.
if (typeof(T) is Base)
throw new NotSupportedException();
// ...
}
}
According to the MSDN the keyword where restricts the type parameter T to be of type Base or to inherit from this class.
[...] a where clause can include a base class constraint, which states that a type must have the specified class as a base class (or be that class itself) in order to be used as a type argument for that generic type.
Also this code does compile:
public static T GetFirst()
where T : Base
{
// Call GetList explicitly using Base as type parameter.
return (T)GetList<Base>().First();
}
So when following the last code typeof(T) should return Base, shouldn't it? Why does Visual Studio then prints this warning to me?
warning CS0184: The given expression is never of the provided ('Demo.Base') type.
typeof(whatever) always returns an instance of type Type. Type doesn't derive from Base.
What you want is this:
if(typeof(T) == typeof(Base))
throw new NotSupportedException("Please specify a type derived from Base");
Something that looks like it is the same is this:
if(variableOfTypeT is Base)
But that has a different meaning.
The first statement (with typeof(Base)) only is true if T is Base. It will be false for any type derived from Base.
The second statement (variableOfTypeT is Base) is always true in your class, because any class derived from Base will return true for a check for its base class.
That's not how you check inheritance.
typeof(T) is of type System.Type, it's not Base. To see if T is derived from Base you should use the IsSubclassOf method, like this:
if(typeof(T).IsSubclassOf(typeof(Base)) ...
In Java, it is possible to bind the type parameter of a generic type. It can be done like this:
class A<T extends B> {
...
}
So, the type parameter for this generic class of A should be B or a subclass of B.
I wonder if C# has a similar feature. I appreciate if somebody let me know.
Thanks
The same in C# is:
class A<T> where T : B
{
}
Also see "Constraints on Type Parameters" (msdn) for a great overview of constraints in general.
Very similar:
public class A<T> where T : B
{
// ...
}
This can be used to constrain T to be a sub-class or implementation of B (if B is an interface).
In addition, you can constrain T to be reference type, value type, or to require a default constructor:
where T : class // T must be a reference type
where T : struct // T must be a value type
where T : new() // T must have a default constructor
Of course you can:
class A<T> where T: B
{
// ...
}
Yes, you can do this, it's called type constraints. Here is an article that explains how:
http://msdn.microsoft.com/en-us/library/d5x73970.aspx
A colleague of mine posted a question on an internal forum which got me thinking about whether this was possible through C#. Basically, he's got an interface as follows:
public interface IProvider<T>
{
T GetT();
}
Is it possible to use something that implements that interface as a type parameter to another generic class and have access to the type T without re-specifying it? For example:
public class Foo<P> where P : IProvider<T>
{
P p;
T GetInnerT() { return p.GetT(); }
}
This does not compile, because the type T is not defined and hence can't be used as a parameter for IProvider. Is something like this even possible? Just curious!
No, he'd need Foo to be generic in both P and T:
public class Foo<P, T> where P : IProvider<T>
otherwise there's no T for the IProvider<T> constraint to be aware of - the fact that it's part of the declaration of IProvider<T> is coincidental. The T in the above declaration is entirely separate (from the compiler's point of view) from the T in IProvider<T>. For example, this would be an equivalent declaration:
public class Foo<TProvider, TProvided> where TProvider : IProvider<TProvided>
Another thing to bear in mind is that an implementation of IProvider<T> could implement it multiple times, for different type arguments:
public class BigProvider : IProvider<string>, IProvider<int>, IProvider<char>
Now what would Foo<BigProvider> mean? It would be ambiguous... whereas with my declaration above, you'd do:
var x = new Foo<BigProvider, int>();
to mean the int-providing aspect of BigProvider.
No, it's not possible, because your definition doesn't allow for the provision of type T when declaring Foo. Consider:
var x = new Foo<string>();
What is T in this case? There is no way to know at compile-time nor at runtime.
As you yourself said: the type T is not defined, and for the class to be valid you have to provide a means of definition. For example:
public class Foo<P, T> where P : IProvider<T>