I have two classes, a base class and a child class. In the base class i define a generic virtual method:
protected virtual ReturnType Create<T>() where T : ReturnType {}
Then in my child class i try to do this:
protected override ReturnTypeChild Create<T>() // ReturnTypeChild inherits ReturnType
{
return base.Create<T> as ReturnTypeChild;
}
Visual studio gives this weird error:
The type 'T' cannot be used as type parameter 'T' in the generic type or method 'Create()'. There is no boxing conversion or type parameter conversion from 'T' to 'ReturnType'.
Repeating the where clause on the child's override also gives an error:
Constraints for override and explicit interface implementation methods are inherited from the base method, so they cannot be specified directly
So what am i doing wrong here?
This works. You had to make the return type generic:
public class BaseClass {
public virtual T Create<T>() where T : BaseClass, new() {
var newClass = new T();
//initialize newClass by setting properties etc
return newClass;
}
}
public class DerivedClass : BaseClass {
public override T Create<T>() {
var newClass = base.Create<T>();
//initialize newClass with DerivedClass specific stuff
return newClass;
}
}
void Test() {
DerivedClass d = new DerivedClass() ;
d.Create<DerivedClass>();
}
These are some basic C# override rules:
The overridden base method must have
the same signature as the override
method.
This means the same return type and same method arguments.
Your override cannot change the return type, even if the return type derives from the base class method's return type. You have to do something like what Igor did above, and make the return type generic.
Related
I get the error message "cannot implicitly convert from type 'SpecificT' to type 'The.Right.Namespace.SpecificT' in the method ToViewDocument.
It seems that in this method I'm redefining the type SpecificT.
This is the methods that gives me troubles:
public override SpecifiedT
ToViewDocument<SpecifiedT>(XmlEntity entity, DocKey docKey)
{
switch (docKey.IdArchive)
{
case IdArchive.A:
return ToAViewData((XmlDocA)entity, docKey) //error at this line;
default:
return ToBViewData((XmlDocB)entity, docKey) //error at this line;
}
}
private SpecifiedT ToAViewData(XmlDocMassima entity, DocKey documentKey)
{
SpecifiedT result = new SpecifiedT();
//map result on entity result
//...
return result;
}
that function overrides the base class' method:
public abstract T ToViewDocument<T>(XmlEntity entity, DocKey documentKey) where T : ViewDocumentDto, new();
Moreover SpecifiedT extends T so I can't understand why the compiler cannot recognize SpecifiedT as a subclass of T.
You can't override a generic method and specify the type parameter. You could add the type parameter to the containing class. Then when you implement the class the type parameter will be set to the concrete type.
abstract class Container<T>
where T : ViewDocumentDto, new()
{
public abstract T ToViewDocument(XmlEntity entity, DocKey documentKey);
}
class SpecificContainer : Container<SpecificViewDocumentDto>
{
public override SpecificViewDocumentDto ToViewDocument(XmlEntity entity, DocKey documentKey)
{
}
}
I'd like to override a generic method in a derived class. The catch is I'd like to have a concrete type parameter implementation as so:
namespace Stumped
{
public class Generic<T> where T : new()
{
public virtual T Foo()
{
return new T();
}
public virtual TAnother GenericMethod<TAnother>() where TAnother : new()
{
return new TAnother();
}
}
public class Concrete : Generic<Inner1>
{
// Concrete return type. Works as expected.
public override Inner1 Foo()
{
return base.Foo();
}
// Why doesn't this make sense? Shows 'Type parameter "Inner2" hides class "Inner2"'.
public override Inner2 GenericMethod<Inner2>()
{
return base.GenericMethod<Inner2>();
}
}
public class Inner1 { }
public class Inner2 { }
}
As mentioned, the compiler tells me:
Type parameter "Inner" hides class "Inner"
I would expect my implementation to work, instead of having to use another generic parameter in this derived class.
Why doesn't this make sense?
You can't do that at all.
Overriding a method cannot change any part of the method's interface. Since GenericMethod<OtherClass>() is valid for the base class, it must be valid for the derived class too.
This is called the Liskov substitution principle
.
As the compiler is warning you, you actually just declared a normal type parameter that happens to have the same name as your class.
I have a Base class with a template. Within this class there is an abstract method with a return type of the type in the template (see below).
I wish to create a new class , Derived , which inherits from this Base class , which (as expected) must "override" that method.
My question is how do I declare and implement the Derived class and the "overridden" method ?
Thanks allot in advance,
Guy.
public abstract class Base<MyType>
{
protected abstract MyType Foo();
}
public class Derived : Base ?????
{
protected override MyType Foo() ?????
{
return new MyType();
}
}
Simply specify the actual type for the generic base class, i.e something like:
public class Derived : Base<MyType>
{
protected override MyType Foo()
{
// some implementation that returns an instance of type MyType
}
}
Where MyType is the actual type you want to specify.
The other option would be to keep the derived class as generic, so something like:
public class Derived<T> : Base<T>
{
protected override T Foo()
{
// some implementation that returns an instance of type T
}
}
You must specify concrete type for Base or make Derived also generic:
public class Derived : Base<int>
{
protected override int Foo();
{
return 0;
}
}
or generic version:
public class Derived<TMyType> : Base<TMyType>
{
protected override TMyType Foo();
{
return default(TMyType);
}
}
Declare it in the same way:
public class Derived<MyType> : Base<MyType>
{
protected override MyType Foo()
{
return new MyType();
}
}
Suppose I have a generic method that is made generic just for the purpose of returning correct type so upstream callers don't have to cast returned values.
public T Add<T>(string name, string details, ...)
where T: BaseClass
{
// somehow get correct ObjectType
ObjectType type = ??????;
T result = Repo.Add<T>(type, name, details, ...);
...
return result;
}
Problem
The problem of this generic method is that I'm not getting an instance of a concrete class represented with generic type. This means that callers of this method have to explicitly provide generic type as type inference can't be done.
public abstract class BaseClass
{
public abstract ObjectType ActualType { get; }
...
}
Implemented child classes define this property as a quasy constant
public class ConcreteClass: BaseClass
{
public override ObjectType ActualType
{
get { return ObjectType.SomeType; }
}
...
}
Question
Based on generic method call
var result = Add<ConcreteClass>("title", "details");
how can I get the value of ActualType within my Add<T> method? I also tried adding new() generic type constraint, but that doesn't compile as my BaseClass is abstract, so I'm unable to call
new T();
within my method to get that ActualType value.
You need to apply the new() constraint to your Add method like this:
public T Add<T>(string name, string details)
where T: BaseClass, new()
{
T result = new T();
//snip
return result;
}
public abstract class BaseClass { /* snip */ }
public class ConcreteClass: BaseClass { /* snip */ }
Which means this code will work:
var thing = Add<ConcreteClass>("Fred", "Lives in France");
According to the MSDN documentation on the new constraint:
The new constraint specifies that any type argument in a generic class
declaration must have a public parameterless constructor. To use the
new constraint, the type cannot be abstract.
This refers to the type passed in, not the other constraint on your method.
Why is this not possible?
abstract class A
{
public abstract T f<T>();
}
class B<T> : A
{
public override T f()
{
return default (T);
}
}
Errors:
does not implement inherited abstract member 'A.f<T>()'
no suitable method found to override
I know that the signature must be same, but from my point of view I see no reason what could possibly be wrong that this is forbidden.
Also I know that another solution is to make A generic, rather than its method, but it is not suitable for me for some reason.
This is not possible because those methods have different signatures. A.f is generic method and B.f is not (it merely uses class generic argument).
You can see this form caller perspective:
A variableA = new A();
variableA.f<int>();
B<int> variableB = new B<int>();
variableB.f();
B does not fulfil the contract of A.
A allows f to be called with any type parameter to return that type. B doesn't allow f to be called with a type parameter, and just returns the type of B's type parameter.
For example, say you had a B<int> and cast it to an A (which should be possible as it inherits from it). Then you called f<bool>() on it (which should be possible as it's an A). What then? The underlying B<int> doesn't have a method to call.
B b = new B<int>();
// This is legal as B inherits from A
A a = b;
// This is a legal call, but how does b handle it?
bool result = a.f<bool>();
In the case of your code
abstract class A
{
public abstract T f<T>();
}
class B<T> : A
{
public override T f()
{
return default (T);
}
}
what do you expect to be called in the below code
public void Foo(A myObj) {
myObj.f<DateTime>();
}
Foo(new B<int>());
There's no implementation for that method though the type contract (the abstract class A) clearly states that you need an implementation. So you can either implement or change the contract to use a type argument at the class level
abstract class A<T>
{
public abstract T f();
}
class B<T> : A<T>
{
public override T f()
{
return default (T);
}
}
does compile however it also limits f of course
Probably this is what you intend to do:
abstract class A
{
public abstract T f<T>();
}
class B<U> : A
{
public override T f<T>() //also needs to have a generic type parameter
{
throw new NotImplementedException();
}
public U f()
{
return f<U>();
}
}
The generic method type parameter and the generic class type parameter (here T and U) have no straightforward connection, i.e. T is not restricted to be U (or something) in the base class and you cannot change this restriction in the derived class.
abstract class A
{
public abstract T f<T>();
}
class B<T> : A
{
public override T f<T>()
{
return default (T);
}
}