I have some problem with delegates
My scheme of classes:
public interface IWorker<T> where T : IModel
{
T Do(T model);
T ReadyToWork(T model);
}
public abstract class Workers<T> : IWorker<T> where T : IModel
{
public abstract T Do(T model);
public abstract T ReadyToWork(T model);
}
Class to work! FirstModel : IModel
public class FirstWorker : Workers<ModelFirst>
{
public override ModelFirst Do(ModelFirst model)
{
return new ModelUserFirst();
}
public override ModelFirst ReadyToWork(ModelFirst model)
{
throw new NotImplementedException();
}
}
I can create many work specials classes, and to work with them I created one access point:
public class WorkPoint<T> where T : IModel
{
public static Func<T, T> Do { get; set; }
public static Func<T, T> ReadyToWork{ get; set; }
public WorkPoint(ModelFirst mod)
{
Do = FirstWorker.Instance().Do;
ReadyToWork= FirstWorker.Instance().ReadyToWork;
}
}
ok, and question. on moment assignments Do and ReadyToWork I catching error
Error CS0123 No overload for 'Do' matches delegate 'Func'
Whats wrong?
It's not the most elegant solution, but this works:
public class WorkPoint<T> where T : IModel
{
public Func<T, T> Do { get; }
public Func<T, T> ReadyToWork { get; }
protected WorkPoint(Func<T, T> f, Func<T, T> r)
{
Do = f;
ReadyToWork = r;
}
}
// Create a simple class like this for evert worker type you need
public class FirstWorkPoint : WorkPoint<ModelFirst>
{
private FirstWorkPoint() :
base(FirstWorker.Instance().Do, FirstWorker.Instance().ReadyToWork)
{ }
// Public method to get a new base instance
public static WorkPoint<ModelFirst> New() => new FirstWorkPoint();
}
This will add a bit of overhead due to the additional class created, but it should act as a workaround for that covariance issue being pointed out in the comments.
This way you could just do:
WorkPoint<ModelFirst> point = FirstWorkPoint.New();
Edit: Also, if you don't need that base Workers<T> class I'd say you could just get rid of it and have the actual worker classes directly inherit from your interface. After all, the base class doesn't provide any additional functionalities in the above code.
Related
Given a set of services that each implement a generic interface where the type parameters all have a common base, how can I write a factory method to return an instance of one of these services? There seems to be no way to write a return type for the factory method.
Here is the sample code, with the problematic method at the bottom:
public abstract class Base
{
public int BaseProp { get; set; }
}
public class Derived1 : Base
{
public int DerivedProp1 { get; set; }
}
public class Derived2 : Base
{
public int DerivedProp2 { get; set; }
}
public interface IHandleStuff<T> where T : Base
{
T GetStuff();
void DoStuff(T t);
}
public class Service1 : IHandleStuff<Derived1>
{
public Derived1 GetStuff() => new();
public void DoStuff(Derived1 t){}
}
public class Service2 : IHandleStuff<Derived2>
{
public Derived2 GetStuff() => new();
public void DoStuff(Derived2 t){}
}
public class Consumer
{
public void DoStuff(Base t)
{
var service = GetServiceFor(t);
service.DoStuff(t);
}
private IHandleStuff<Base> GetServiceFor(Base t)
{
return t.BaseProp switch
{
1 => new Service1(), // Cannot convert expression type 'Service1' to return type 'IHandleStuff<Base>'
2 => new Service2(), // An explicit cast to IHandleStuff<Base> compiles but fails at runtime
_ => throw new ArgumentOutOfRangeException()
};
}
}
Update:
Someone pointed out in a comment (now deleted) that the issue in the above code can be resolved by making the DoStuff and GetServiceFor methods in the Consumer class generic. This works, but at some point in the real code we have to call into this from a non-generic method which knows only the base type. So the suggestion only defers the problem.
public abstract record Result<T, ErrorT>
{
private Result() { }
public sealed record Ok(T result) : Result<T, ErrorT>;
public sealed record Error(ErrorT error) : Result<T, ErrorT>;
}
public interface IValid<T>
{
T Value { get; init; }
abstract static IEnumerable<string> Validate(T obj);
abstract static Result<IValid<T>, IEnumerable<string>> Create(T value);
}
I am trying to create a validation pattern that creates validated values of type T. But the Create function throws a compiler error CS8920
The interface Valid cannot be used as type argument
Any reason why this is not allowed and is there a way around this?
Example usage of this would be
public record DriverLicense : IValid<string>
{
public string Value { get; init; } = null!;
private DriverLicense() { }
public static Result<DriverLicense, IEnumerable<string>> Create(string licenseNumber){...}
public static IEnumerable<string> Validate(string licenseNumber){...}
}
This code alone:
public abstract record Result<T, ErrorT>
{
private Result() { }
public sealed record Ok(T result) : Result<T, ErrorT>;
public sealed record Error(ErrorT error) : Result<T, ErrorT>;
}
public interface IValid<T>
{
T Value { get; init; }
abstract static IEnumerable<string> Validate(T obj);
abstract static Result<IValid<T>, IEnumerable<string>> Create(T value);
}
did compile when they first added static abstract interface members, as can be seen on SharpLab, if you select the "C# Next: Static Abstract Members In Interfaces" branch.
However, this signature for Create is not what you actually mean. Your intended implementation doesn't actually implement Create:
// interface
abstract static Result<IValid<T>, IEnumerable<string>> Create(T value);
// implementation
public static Result<DriverLicense, IEnumerable<string>> Create(string licenseNumber){}
Notice that the return types are different, unrelated types. The type parameters of records are invariant. This would not have compiled in any version of C#.
What you actually meant is:
public interface IValid<TSelf, T> where TSelf: IValid<TSelf, T>
{
...
abstract static Result<TSelf, IEnumerable<string>> Create(T value);
}
public record DriverLicense : IValid<DriverLicense, string>
{
...
public static Result<DriverLicense, IEnumerable<string>> Create(string licenseNumber) {...}
}
See also:
Curiously recurring template pattern
A similar question
My guess is that they changed it in a later version, so that an error message would now show up as soon as you try to write the incorrect interface, rather than show the "does not implement this method" error when you write the implementation later down the line.
Given the following classes (factories used because C# doesn't support type inference on constructors):
public class A<T>
{
public A(B<T> b) { }
}
public class B<T>
{
public B(C<T> c) { }
}
public class C<T>
{
public C(T tee) { }
}
public class D<T>
{
public static D<T> Create<V>(Expression<Func<T, V>> property)
{
return new D<T, V>(property);
}
}
public class D<T, V> : D<T>
{
public D(Expression<Func<T, V>> property) { }
}
public class Model
{
public int P1 { get; set; }
public string P2 { get; set; }
}
public class AFactory
{
public static A<T> Create<T>(B<T> bee)
{
return new A<T>(bee);
}
}
public class BFactory
{
public static B<T> Create<T>(C<T> cee)
{
return new B<T>(cee);
}
}
public class CFactory
{
public static C<T> Create<T>(params D<T>[] tees)
{
return null;
}
}
The following compiles:
AFactory.Create(BFactory.Create(CFactory.Create(
D<Model>.Create(m => m.P1), D<Model>.Create(m => m.P2)
)));
The following does not:
AFactory.Create<Model>(BFactory.Create(CFactory.Create(
D.Create(m => m.P1), D.Create(m => m.P2)
)));
The difference is that in the first example I'm specifying the type of the model on the innermost classes, so type inference works normally and propagates up the tree. The problem is that I then have to specify the model type on every D.Create() call, which seems redundant.
The second example is the way I'd like to write this code: tell the outermost class that its type is Model and all the classes that are being constructed use that type as well. Essentially, it's syntactic sugar for AFactory.Create<Model>(BFactory.Create<Model>(/* turtles all the way down... */)).
Is there any way to achieve this in C#? I've tried all the permutations of inheritance and type constraints that I can think of, but nothing has given me the desired result.
I'm also fully aware that I might be missing something fundamental about generics - please feel free to educate me if that's the case.
Assume the following classes
// What I have created ...
public abstract class TaxServiceProvider<T, S>
where T : TaxServiceProviderConfig
where S : TaxServiceInfo
{
protected T Config { get; set; }
public abstract S GetTax(int zipCode);
}
public abstract class TaxServiceInfo { ... }
public abstract class TaxServiceProviderConfig { ... }
// What I want to create ...
public class SpecialTaxServiceProvider<T, S> : TaxServiceProvider<SpecialTaxServiceProviderConfig, SpecialTaxServiceInfo>
where T : SpecialTaxServiceProviderConfig
where S : SpecialTaxServiceInfo
{ ... }
public class SpecialTaxServiceInfo : TaxServiceInfo { ... }
public class SpecialTaxServiceProviderConfig : TaxServiceProviderConfig { ... }
where TaxServiceInfo and TaxServiceProviderConfig are used to support the TaxServiceProvider class.
I want to create a derived class SpecialTaxServiceProvder (non-abstract) from TaxServiceProvider that is also generic in the same way that TaxServiceProvider is and takes SpecialTaxServiceInfo and SpecialTaxServiceProviderConfig as the types.
I want to implement GetTax and Config in SpecialTaxServiceProvider so that GetTax returns type SpecialTaxServiceInfo and Config is of type SpecialTaxServiceProviderConfig
I would then create an additional class derived from SpecialTaxServiceProvider and classes derived from SpecialTaxServiceInfo and SpecialTaxServiceProviderConfig
public class A_SpecialTaxServiceProvider : SpecialTaxServiceProvider<A_SpecialTaxServiceProviderConfig, A_SpecialTaxServiceInfo>
{ ... }
public class A_SpecialTaxServiceProviderConfig : SpecialTaxServiceProviderConfig { ... }
public class A_SpecialTaxServiceInfo : SpecialTaxServiceInfo { ... }
where GetTax for this class returns type A_SpecialTaxServiceInfo and the Config for this class is of type A_SpecialTaxServiceProviderConfig
I've looked into covariance in C# and the syntax for generic typed classes but I'm not sure if what I'm trying to do is impossible in the language or I just don't know the proper way to set it up.
Change SpecialTaxServiceProvider and pass T and S to TaxServiceProvider:
public class SpecialTaxServiceProvider<T, S> : TaxServiceProvider<T, S>
where T : SpecialTaxServiceProviderConfig
where S : SpecialTaxServiceInfo
{
public override S GetTax(int zipCode)
{
return null;
}
}
Implement A_SpecialTaxServiceProvider and override GetTax:
public class A_SpecialTaxServiceProvider : SpecialTaxServiceProvider<A_SpecialTaxServiceProviderConfig, A_SpecialTaxServiceInfo>
{
public override A_SpecialTaxServiceInfo GetTax(int zipCode)
{
return null;
}
}
It prevents you from creating
public class A_SpecialTaxServiceProvider : SpecialTaxServiceProvider<A_SpecialTaxServiceProviderConfig, TaxServiceInfo>
with following error:
The type 'Project.TaxServiceInfo' cannot be used as type
parameter 'S' in the generic type or method
'Project.SpecialTaxServiceProvider'. There is no implicit
reference conversion from 'Project.TaxServiceInfo' to
'Project.SpecialTaxServiceInfo'.
I decided to use function delegates in my constructors to allow me to pass in factory methods to create my generic types.
Here is what I ended up with
// Tier 1 of my class hierarchy
public abstract class TaxServiceProvider<C, I>
where C : TaxServiceProviderConfig
where I : TaxServiceInfo
{
protected C Config { get; set; }
public abstract I GetTax(int zipCode);
}
public abstract class TaxServiceInfo {
public TaxServiceInfo(string param1, string param2, int param3, ect...) {
...
}
}
public abstract class TaxServiceProviderConfig { ... }
// Tier 2 of my class hierarchy
public class DerivedTaxServiceProvider<C, I> : TaxServiceProvider<C, I>
where C : DerivedTaxServiceProviderConfig
where I : DerivedTaxServiceInfo
{
protected Func<S, string, string, int, ect...> Factory;
public DerivedTaxServiceProvider (C config, Func<I, string, string, int, ect...> factory) {
Config = config;
Factory = factory;
}
public override I GetTax(int zipCode) {
...
I taxServiceInfo = Factory("param1", "param2", 3, ect...);
...
return I;
}
}
public class DerivedTaxServiceInfo : TaxServiceInfo {
public DerivedTaxServiceInfo(string param1, string param2, int param3, ect...)
: base(param1, param2, param3, ect...)
{ ... }
}
public class DerivedTaxServiceProviderConfig : TaxServiceProviderConfig { ... }
// Tier 3 of my class hierarchy
public class ConcreteTaxServiceProvider : DerivedTaxServiceProvider<ConcreteTaxServiceProviderConfig, ConcreteTaxServiceInfo> {
public ConcreteTaxServiceProvider(ConcreteTaxServiceProviderConfig config, Func<ConcreteTaxServiceInfo, string, string, int, ect...> factory) {
Config = config;
Factory = factory;
}
public override ConcreteTaxServiceInfo GetTax(int zipCode) {
return base.GetTax(zipCode);
}
}
public class ConcreteTaxServiceInfo : DerivedTaxServiceInfo {
public ConcreteTaxServiceInfo(string param1, string param2, int param3, ect...)
: base(param1, param2, param3, ect...)
{ ... }
public static ConcreteTaxServiceInfo CreateConcreteTaxServiceInfo(string param1, string param2, int param3, ect...) {
return new ConcreteTaxServiceInfo(param1, param2, param3, etc...);
}
}
public class ConcreteTaxServiceProviderConfig : DerivedTaxServiceProviderConfig { ... }
// Implementation of my class hierarchies
public void method() {
ConcreteTaxServiceProviderConfig() config = new ConcreteTaxServiceProviderConfig();
ConcreteTaxServiceProvider provider = new ConcreteTaxServiceProvider(config, ConcreteTaxServiceInfo.CreateConcreteTaxServiceInfo);
ConcreteTaxServiceInfo serviceInfo = provider.GetTax(99939);
}
So basically I have 2 levels of generic classes. The 2nd level Provider class overrides the base abstract method for "GetTax", but I didn't want it to return a concrete type because then I couldn't cleanly call that method in a class that inherited from it. I would have had to cast my derived ServiceInfo into a ConcreteServiceInfo type when calling "GetTax" in the concrete (3rd level) Provider.
As long as I have a constructor in an Info class that matches my delegate, no matter at what level of subclassing (below the 2nd level) that Info class is, I can feed it into my provider and use the 2nd level's Provider class GetTax method.
This factory method can look kind of ugly in the parameter lists for the Provider constructors and creating static methods of the Info classes seems awkward, but it does the trick!
Here is an SO question I consulted to come up with this solution : C# generics problem - newing up the generic type with parameters in the constructor
I've got a handy collection in my middle tier which is for collections of child things that belong to a parent thing.
public class ChildCollection<TParent, TChild>
{
public IEnumerable<TChild> GetChildren();
etc.
}
In the interface, I've got a handy grid that can display the contents of a ChildCollection<TParent,TChild> and let users do work on it.
public abstract class ChildCollectionGrid<TCollection, TParent, TChild> : MyGridControl
where TCollection : ChildCollection<TParent, TChild>
{
public abstract TCollection Collection;
etc.
}
Inheriting this class to make a grid to work with the Waffles on a Widget ends up looking like this.
public class WidgetWafflesGrid : ChildCollectionGrid<WidgetWafflesCollection, Widget, Waffle>
This is a little redundant. A WidgetWaffleCollection is a ChildCollection<Widget,Waffle>. With that first generic type argument specified, the class won't compile unless you specify exactly those two others.
Is there a prettier way to accomplish this where the compiler could infer those other two types? I know I'm being finicky but ideally I would like to have the class declaration look like:
public class WidgetWafflesGrid : ChildCollectionGrid<WidgetWafflesCollection>
Thanks for your help!
No, there's not. Generic parameter inference works only on methods.
Why derive from your collection? Just keep it like:
public abstract class ChildCollectionGrid<TParent, TChild> : MyGridControl
{
public abstract ChildCollection<TParent, TChild> Collection;
etc.
}
public class WidgetWafflesGrid : ChildCollectionGrid<Widget, Waffle>
{
}
The only way to handle inheritance in collections with Generics is using the Collection<TCollection,TChild> : where TCollection : Collection<TCollection,TChild> { } pattern.
Here is an example with a concrete class
public abstract class Collection<TCollection, TChild>
where TCollection : Collection<TCollection, TChild>, new()
{
protected Collection()
{
List=new List<TChild>();
}
protected List<TChild> List { get; set; }
public TCollection Where(Func<TChild, bool> predicate)
{
var result=new TCollection();
result.List.AddRange(List.Where(predicate));
return result;
}
public void Add(TChild item) { List.Add(item); }
public void AddRange(IEnumerable<TChild> collection) { List.AddRange(collection); }
}
public class Waffle
{
public double Temperature { get; set; }
}
public class WafflesCollection : Collection<WafflesCollection, Waffle>
{
public WafflesCollection BurnedWaffles
{
get
{
return Where((w) => w.Temperature>108);
}
}
}
class Program
{
static void Main(string[] args)
{
WafflesCollection waffles=new WafflesCollection();
// Count = 3
waffles.Add(new Waffle() { Temperature=100 });
waffles.Add(new Waffle() { Temperature=120 });
waffles.Add(new Waffle() { Temperature=105 });
var burned=waffles.BurnedWaffles;
// Count = 1
}
}