beakona / AutoInterface

C# interface-to-member source generator
MIT License
73 stars 9 forks source link

Feature request: auto generate `public IMyInterface AsMyInterface()` methods #19

Closed jvmlet closed 7 months ago

jvmlet commented 7 months ago

Proposal : Given the class

[GenerateAutoAs]
public partial class MyClass : IMyInterface1, IMyInterface2{
.... implementation
}

generates

public partial class MyClass : IMyInterface1, IMyInterface2{
  public IMyIntrface1 AsMyInterface1(){ return this};
  public IMyIntrface2 AsMyInterface2(){ return this};
}
beakona commented 7 months ago

How would it be used? This would give fluent syntax. Casting and LINQ Cast<T>() are not equivalent because this is compile-type-safe. Is there more to it?

Here are some quick thoughts. AutoAs could have additional parameters [GenerateAutoAs(Sources=AutoAsSources.All, InterfaceType=typeof(IMyInterface1))]

Using Sources consumer could filter which AsX method should be generated. Here are some flags:

Example with unknown generic tied to class:

public partial class MyClass<T> : IMy1<T> {}
public partial class MyClass<T> {
   public IMy<T> AsMy1() => this;
}

Example with known generic:

public partial class MyClass : IMy1<int> {}
public partial class MyClass {
   public IMy<int> AsMy1() => this;
}

Problematic example:

public partial class MyClass : IMy1<int>, IMy1<string> {}
public partial class MyClass {
   public IMy<int> AsMy1() => this;
   public IMy<string> AsMy1() => this;
}

Another problematic example:

public partial class MyClass : IMy1<int>, IMy1<int, int> {}
public partial class MyClass {
   public IMy<int> AsMy1() => this;
   public IMy<int, int> AsMy1() => this;
}
jvmlet commented 7 months ago

Thanks for the quick response. The intention is to allow easier API exploration, no need to Show me your type hierarchy to ISomeFeature someFeature = obj; someFeature.Invoke(), instead , just obj.AsSomeFeature().Invoke().

About problematic use-cases - just post-index the ambiguous types :

public partial class MyClass : IMyInterface, IMyInterface<int>, IMyInterface<string>, IMyInterface<int, int> {}
public partial class MyClass {
   public IMyInterface AsMyInterface_0() => this;
   public IMyInterface<int> AsMyInterface_1() => this;
   public IMyInterface<string> AsMyInterface_2() => this;
   public IMyInterface<int, int> AsMyInterface_3() => this;
}

I would say that the strategy would be EntireInterfaceHierarchy | SkipBaseSystemInterfaces

jvmlet commented 7 months ago

Another thought - the top level interface types from this and base class are enough, because you can call interface's inherited methods :

public interface IA{
 public void DoA() ;
} 

public interface IB:IA{
 public void DoB() ;
} 
public class B:IB{
} ;
new B().AsB().DoA():
beakona commented 7 months ago

I've made 1.0.32-pre preview with requested feature.

Source generator have two parameters: EntireInterfaceHierarchy=false, SkipSystemInterfaces=true. Those are default values. In the case of ambiguous method names every method will get _N suffix which is stable-sorted (by the number or generic arguments, then by the name of the type).

In the case of name conflict (targeted method name already exists) generator should make compiler error but that part is not implemented yet. Please note that some compiler error codes are not set up correctly, I will do this tomorrow.

jvmlet commented 7 months ago

Great, thanks, I will give it a try next week.

jvmlet commented 7 months ago

@beakona , just one comment :

EntireInterfaceHierarchy - take base interface for every interface

is redundant, because no need to cast interface to base interface to call it's method. You can new B().AsB().DoA();

beakona commented 7 months ago

Yes, it's redundant, but if someone don't know type hierarch and want to explore interfaces it could opt-in for all interfaces.

beakona commented 7 months ago

I've made separate git repository AutoAs specificaly for this source generator. I'm planning to remove it from AutoInterface because each time compiler activates this source generator it would pass semantic elements twice, first time to AutoInterface and second time to GenerateAutoAs.

jvmlet commented 7 months ago

Thanks for quick implementation, @beakona , I find it very useful. Pure joy !!!