In the course of eliminating a catastrophic gap in knowledge, I sketched a certain class, something like this (I don’t include designers and methods in the listing, the question is difficult to understand);

public class SomeFractal { public int Generation; public SomeFractal Parent { get; private set; } public SomeFractal Root { get { if (Generation == 0) return this; else return Parent.Root; } } List<Fractal> Children {get;} } 

This was done to refine the recursive algorithms, but I'm not talking about that.

There was an idea to describe several "fractal" classes, differing in details, and implementing a single interface, let it be IFractal. The interface must contain basic properties (parent, root, child branches).

Most properties naturally return a class object in which they are described (we have a fractal, remember?), Respectively, if I want them described in the interface, it must be covariant. It turns out like this:

 interface IFractal <out T> where T : IFractal<T> { T Root { get; } T Parent { get; } IEnumerable<T> Children { get; } } 

Actually, there are no problems - the interface, of course, is implemented by the class described above:

 class SomeFractal : IFractal<SomeFractal> 

The problems will begin if I want somewhere else to use a variable of type IFractal<T> to abstract away from the concrete implementing class. After all, I will have to specify the parameter T, which can only be a class that implements IFractal - that is, no abstraction is working; in this case it’s the same thing as using the class itself.

Are there any ways around this limitation? Maybe I could not in covariance?

Please do not discuss the feasibility of practical application, it is pure theorizing for educational purposes.

UPDATE

I will explain about non-generics with an example

 interface IFractal { IFractal Root { get; } IEnumerable<IFractal> Children { get; } } class Fractal : IFractal { public IFractal Root { get { if (parent == null) return this; else return parent.Root; } } public IEnumerable<IFractal> Children { get; } private Fractal parent; private string prettyCoolField; public string GetRootField() { return Root.prettyCoolField; } } 

GetRootField() Will not work, because IFractal does not have a prettyCoolField field. return (Fractal)Root.prettyCoolField; also does not work. Or am I incorrectly driven type?

  • And why do you need covariance? This is not a niggle, but a theoretical question for educational purposes. If you work with the INonGenericFractal abstract interface, what do you lack, what do you need covariance for? IEnumerable<INonGenericFractal> and Root / Parent getters are both covariant. - VladD
  • Well, here it is not considered good form to make significant changes in the question, especially after the answer is given: this puts the respondent in a stupid position, as it were. (Is it possible that your editing does not invalidate the answer, then everything is fine.) - VladD
  • @VladD, only the listing was significantly changed, in accordance with the comments of Paul. Also reformulated the title. The question asked in the penultimate paragraph remained unchanged. - eastwing
  • @VladD, IEnumerable<INonGenericFractal> and getters will be of type INonGenericFractal (interface), not NonGenericFractal (implementing class) - that is, it’s already like not a fractal - eastwing
  • @VladD made an update with an example of a non-universal interface - eastwing

2 answers 2

If you want on the one hand a class that represents an element of the tree, a tree, and on the other hand, an interface for reading that does not depend on the class itself, then you need to separate the data and implementation of the interface.

There are no problems, since getter-only-properties and IEnumerable<T> are covariant in type T As a result, we get the usual interface:

 interface IFractal { IFractal Root { get; } IFractal Parent { get; } IEnumerable<IFractal> Children { get; } } 

and its implementation:

 class Fractal : IFractal { public Fractal Root { get; } public Fractal Parent { get; } public IEnumerable<Fractal> Children { get; private set; } IFractal IFractal.Root => Root; IFractal IFractal.Parent => Parent; IEnumerable<IFractal> IFractal.Children => Children; } 

    Usually they do this:

     interface IFractal<T> where T : IFractal<T> 

    This will allow walking on the "navigation" properties without knowing the specific type of T

    • I corrected my code, but it does not answer the question. I still need to specify T if I want to declare a variable of type IFractal <T> - eastwing
    • @eastwing can use generalized methods - Pavel Mayorov
    • did not quite understand - where to use? If not difficult, give an example - eastwing