Method constraint must be super of class generic - c#

How can I do the following?
I have a method which I need to say its generic type must be the super of the classes generic type.
Here is an example:
class Logger : ILogger, IStartable {}
///use
new FluentBuilder<Logger>().As<ILogger>().As<IStartable>();
This shows my intent, however does not work (as it is not syntactically incorrect):
public class FluentBuilder<TService> where TService : class
{
public FluentBuilder<TService> As<TContract>() where TService : TContract
{
return this;
}
}

This is not exactlywhat you want, but maybe a step in the correct direction. You can implement your As method as extension method:
public static class Ex
{
public static FluentBuilder<TService> As<TService, TContract>(this FluentBuilder<TService> that)
where TContract : class
where TService : class, TContract
{
return that;
}
}
The usage syntax is then:
new FluentBuilder<Logger>().As<Logger, ILogger>().As<Logger, IStartable>();

Related

Inherit Generic classes

I have a class like below. But I don't know how to inherit it in another class, I need to override a method in this class. I tried many ways but it seems that the parameters TUser and TKey are still in the wrong syntax. How to inherit similar classes?
public class AspNetIdentityUserService<TUser, TKey> : UserServiceBase
where TUser : class, Microsoft.AspNet.Identity.IUser<TKey>, new()
where TKey : IEquatable<TKey>
{
protected readonly Microsoft.AspNet.Identity.UserManager<TUser, TKey> userManager;
protected readonly Func<string, TKey> ConvertSubjectToKey;
public AspNetIdentityUserService(
Microsoft.AspNet.Identity.UserManager<TUser, TKey> userManager,
Func<string, TKey> parseSubject = null)
{
// initilaization
}
//More Methods
}
I tried inheriting like
public class EnforcingLocalSignup<TUser, TKey> :
AspNetIdentityUserService<TUser, TKey> where TUser : class,
Microsoft.AspNet.Identity.IUser<TKey>, new() where TKey : IEquatable<TKey> { }
but it fails with
Error CS7036 There is no argument given that corresponds to the required formal parameter 'userManager' of 'AspNetIdentityUserService<TUser, TKey>.AspNetIdentityUserService(UserManager<TUser, TKey>, Func<string, TKey>)'
Here's brain dead simple code that repros your error:
public class BaseClass
{
public BaseClass(string userManager)
{
UserManager = userManager;
}
public string UserManager { get; }
}
public class SubClass : BaseClass
{
}
Which results in this error (pointing to the public class SubClass : BaseClass line):
error CS7036: There is no argument given that corresponds to the required formal parameter 'userManager' of 'BaseClass.BaseClass(string)'
This happens because the base class has no default constructor, only the one that requires a userManager parameter.
That's the same error you are seeing. Note that it has nothing to do with the complicated generic nature of your classes, only with how the constructors are, um, constructed. Subclass, since it doesn't define a constructor, gets the default default constructor (i.e., one that just sets all properties to their default values).
If I add a default constructor:
public SubClass () { }
I get the same error, this time pointing to that line of code. However, if I create a constructor like that calls the one-and-only base class constructor:
public SubClass(string userManager) : base (userManager)
{
}
The error goes away.
When you define a class without defining any constructor, a parameterless constructor is implicitly defined.
Basically this:
public class MyClass { }
Actually is interpreted by the compiler like this:
public class MyClass : System.Object
{
public MyClass() : base() { }
}
This works because System.Object class has a parameterless constructor, that you can call via base().
When a class defines a constructor with parameters but not a parameterless constructor, the compiler won't generate a parameterless constructor, so this code:
public class BaseClass
{
public BaseClass(int parameter)
{
// ...
}
}
Actually is interpreted (and compiled) as:
public class BaseClass : System.Object
{
public BaseClass(int parameter) : base()
{
// ...
}
}
Now, if you inherit from a class that is missing a parameterless constructor without defining a constructor, like this:
public class MyClass : BaseClass { }
What the compiler "sees" is actually this:
public class MyClass : BaseClass
{
public MyClass() : base() { }
}
But BaseClass do not have a constructor that takes no parameters, hence the compilation error.
It's easily fixed by either define a parametered constructor in your derived class that matches the constructor of base class:
public class MyClass : BaseClass
{
public MyClass(int parameter) : base(parameter) { }
}
or a parameterless constructor that passes a default value to base constructor:
public class MyClass : BaseClass
{
public MyClass() : base(0) { }
}
depending on your design requirements.
Wrapping up and applying to your case, you can fix with:
public class EnforcingLocalSignup<TUser, TKey> : AspNetIdentityUserService<TUser, TKey>
where TUser : class, Microsoft.AspNet.Identity.IUser<TKey>, new()
where TKey : IEquatable<TKey>
{
public EnforcingLocalSignup(Microsoft.AspNet.Identity.UserManager<TUser, TKey> userManager,
Func<string, TKey> parseSubject = null)
{ }
}
When inheriting from, towards or between generic classes, you have 3 options for any of the generic parameters:
a) Expose them on the new class as well. Hand them through:
class MySpeciaList<T> : List<T>{
//You propably want to write something additional here
}
Do not that constraints on MySpeciaList<T> must be at least as restrictive as on the class you inherit from.
b) Hardcode the type. Basically you "de-generezie" the class
class FormList : List<Form> {
//You do not need to add anything here, but maybe want to
//We have to use that trick for XAML, as generics types are not really useable in XAML code
//Note that .NET does have a "Formlist" type, it is from the pre-generic days
}
c) you can of course add generic parameters, that only the new class can use. Indeed you propably do that impicitly:
class SomethingGeneric<T> : object {
//You most definitely should add something here. Ideally something using T
}
All of these can be combined. You can hand through, hardcode and add any number of generic placeholders. Do not that you propably want to use type aliases and var, to keep the code readable.

Generic type constraints - can I extend class constraint on method level?

I am working on some kind of a fluent API which would easily allow to register all necessary classes in the IoC container. For example:
builder
.WithInput<EncryptedMessage>()
.UseEncryptedMessageParser()
// ...
// .OtherEncryptedMessageFlowRegistrations()
builder
.WithInput<StandardMessage>()
.UseStandardMessageParser()
// ...
// .OtherStandardMessageFlowRegistrations()
What I'd like to achieve is to have less strict generic type constraints at the beginning, but make it more strict later in the hierarchy. Therefore, I am wondering if it's somehow possible to create a class with generic type constraints and then extend this generic type with additional constraints on method level?
public class A<T> where T : class
{
public void Foo(T input) where T : SomeBaseClass // additional constraints for generic type on class level
{
}
}
More realistic example:
public class MessageFlowBuilder<TInput> where TInput : class, new()
{
private readonly IServiceCollection _services;
public MessageFlowBuilder(IServiceCollection services)
{
_services = services;
}
public StandardMessageFlowBuilder<TInput> UseStandardMessageParser()
where TInput : StandardMessageBase // additional constraints for generic type on class level (required by parser)
{
_services.AddTransient<StandardMessageParser<TInput>>();
return new StandardMessageFlowBuilder<TInput>();
}
public EncryptedMessageFlowBuilder<TInput> UseEncryptedMessageParser()
where TInput : EncryptedMessageBase // additional constraints for generic type on class level
{
_services.AddTransient<EncryptedMessageParser<TInput>>();
return new EncryptedMessageFlowBuilder<TInput>();
}
}
Or maybe is it possible to create another generic type on method level which then would have a constraint to be of the same type as class level argument?
public class A<T> where T : class
{
public void Foo<T2>(T2 input) where T2 : SomeBaseClass // + some T == T2 type equality?
{
}
}
Could could make Foo a generic method, yes - but you can't constraint it to be the same as T, and you can't add a constraint to an existing type parameter.
Could you try something like this? I am not even sure right now whether this will compile fine or not. The method needs to be generic too on its own.
public class A<T> where T : class
{
public void Foo<U>(U input) where U : T, SomeBaseClass, SomeInterface //whatever
{
}
}
public class A<T> where T : class
{
public void Foo(T input) where T : SomeBaseClass // additional constraints for generic type on class level
{
}
}
No, the type T is declared when you instantiate the class, you can't change that at method level.
So by doing:
new A<SomeBase>();
You have effectively given the method the signature:
public void Foo(SomeBase input)

Implementing a Generic Interface and a Non Generic Interface

I have two contracts (one Generic Interface and the other Non-Generic) as follows:
public interface IGenericContract<T> where T : class
{
IQueryable<T> GetAll();
}
public interface INonGenericContract
{
string GetFullName(Guid guid);
}
I have a class implementing both
public class MyClass<T> :
IGenericContract<T> where T : class, INonGenericContract
{
public IQueryable<T> GetAll()
{
...
}
public string GetFullName(Guid guid)
{
...
}
}
Everything is fine until this point when I compile it.
But now when I try using this class I run into this error
"error CS0311: The type 'string' cannot be used as type parameter 'T' in the generic type or method 'ConsoleApplication1.MyClass'. There is no implicit reference conversion from 'string' to 'ConsoleApplication1.INonGenericContract'."
class Program
{
static void Main(string[] args)
{
MyClass<string> myClass = new MyClass<string>(); //Error
}
}
If I do not implement the Non-generic contract it works fine. What could be wrong here ?
Thanks
In your code INonGenericContract is part of generic constraint, as it placed after where.
public class MyClass<T> :
IGenericContract<T> where T : class, INonGenericContract
You likely want that:
public class MyClass<T> :
IGenericContract<T>, INonGenericContract where T : class
you are very close, what you have to do is implement the non generic interface, not put a constrain.
public class MyClass<T> :
IGenericContract<T>, INonGenericContract where T : class
{
public IQueryable<T> GetAll()
{
return null;
}
public string GetFullName(Guid guid)
{
return null;
}
}
now you can do this
MyClass<string> myClass = new MyClass<string>();
According what you show
public class MyClass<T> : IGenericContract<T> where T : class, INonGenericContract
T must implement INonGenericContract and string doesn't implement it. In short, string is not a valid parameter for class MyClass
If what you're looking for is implementing IGenericContract<T> AND INonGenericContract you should have
public class MyClass<T> : INonGenericContract, IGenericContract<T>
there is no need to have where T : class since IGenericContract<T> already has that constraint.

The type cannot be used as type parameter 'T' in the generic type or method 'BaseController<T>'. There is no implicit reference

I'm trying to create a generic to simplify my codes (it's a web api project), but at somehow it's ended up becoming more complicated than I expected. What I'm trying to implement is something like this:
To simplify my whole real code, this is what I've written:
public interface IDatabaseTable { }
public class ReceiptIndex: IDatabaseTable { }
public interface IBackend<T> where T: IDatabaseTable { }
public class Receipts : IBackend<ReceiptIndex> { }
public class Generic<T> : SyncTwoWayXI, IBackend<T> where T:IDatabaseTable { }
public class BaseController<T> : ApiController where T: IBackend<IDatabaseTable>, new () { }
All of the line above created separately in its own file.
When I try to create controller that Inherit from BaseController
public class ReceiptsBaseController : BaseController<Receipts>
I get an error said
The type 'Receipts' cannot be used as type parameter 'T' in the
generic type or method 'BaseController'. There is no implicit
reference conversion from 'Receipts' to 'IBackend'.
I try to find a similar problem and end up with something called Covariance and Contravariance problem. Can anyone give feedback for what I'm trying to do or maybe something that can I do to simplify it.
The easiest way to solve this, without using covariance and contravariance, which has some important implications, is this:
public class BaseController<TBackend, TDatabaseTable>
: ApiController
where TBackend : IBackend<TDatabaseTable>, new()
where TDatabaseTable: IDatabaseTable
{ }
And use it in this way
public class ReceiptsBaseController : BaseController<Receipts, ReceiptIndex>
{
}
The syntax is not so compact, but it works like a charm, without the extra implications of covariance or contravariance.
You can try to specify the T in IBackend.
Like this:
public class BaseController<T, TBackEndSubType> : ApiController
where T : IBackend<TBackEndSubType>, new()
where TBackEndSubType : IDatabaseTable { }
public class ReceiptsBaseController : BaseController<Receipts, ReceiptIndex> { }
On the BaseController you have this condition:
where T: IBackend<IDatabaseTable>
but receipts inhertits IBackend<ReceiptIndex>, which is not directly compatible with IBackend<IDatabaseTable>. You could add 2 generic parameters on your BaseController:
public class BaseController<TBackend, TDatabaseTable> : ApiController
where TDatabaseTable: IDatabaseTable
where TBackend: IBackend<TDatabaseTable>, new () { }
then you can declare your controller like this:
public class ReceiptsBaseController : BaseController<Receipts, ReceiptIndex>
using the out modifier: https://msdn.microsoft.com/en-us/library/dd469487.aspx
Change the IBackend interface to look like this:
public interface IBackend<out T> where T : IDatabaseTable { }

How to Inherit from Generic Parent

I have a parent Class
public class GenericRepository<TEntity> where TEntity : class
{
//Implementation
}
And I want to inherit from this class, but I can't seem to get it right,here are my attempts
public class CustomerRepository<Customer> : GenericRepository<Customer>
{
//implementation
}
Or this,
public class CustomerRepository<T> : GenericRepository<T> where T : new Customer()
{
}
Or this one
public class CustomerRepository<T> : GenericRepository<CustomerRepository<T>> where T : CustomerRepository<T>
{
}
No matter what I do, I get this error. Please show me how I can inherit from this class, classes share the same Namespace
Error 'GenericRepository' does not contain a constructor that takes 0 arguments CustomerRepository.cs
It sounds like you want a non-generic class inheriting from a generic one, like this:
public class CustomerRepository : GenericRepository<Customer>
{
}
If you want this to be a generic class that narrows the type of the generic parameter (only allows Customer or a derived type):
public class CustomerRepository<T> : GenericRepository<T>
where T : Customer
{
}
Regarding your compile-time error:
Error 'GenericRepository<Customer>' does not contain a constructor that takes 0 arguments
This means exactly what it says. You have not defined a constructor in your derived class, which means that a constructor is implicitly generated, as though you had typed this:
public CustomerRepository() : base() { }
However, the base class (GenericRepository<Customer>) does not have a constructor that takes no arguments. You need to explicitly declare a constructor in the derived class CustomerRepository and then explicitly call a constructor on the base class.
You don't need to repeat the type parameter in the deriving class, so:
public class CustomerRepository : GenericRepository<Customer>
{
//implementation
}
Is what you need.
It seems that your base class has no constructor without parameters, if so the derived class must declare a.constructor and call the base class constructor with parameter.
class MyBase { public MyBase(object art) { } }
class Derived : MyBase {
public Derived() : base(null) { }
}
In this example if you remove the ctor from Derived you get the same error.
Use can write as:
public class CustomerRepository : GenericRepository<Customer>
{
//implementation
}

Categories

Resources