This library is to .NET what Lombok is to Java. It generates constructors and other fun stuff using Source Generators for those classes you specify special attributes for. Check out the examples for more info.
At least .NET 5 is required for projects using this library. You can install Lombok.NET either via NuGet
Install-Package Lombok.NET
Or via the .NET Core command-line interface:
dotnet add package Lombok.NET
When building from source in the "Debug" configuration, the build will be suspended until a debugger is attached to the build process.
During this time it is possible to set break points inside the generators or analyzers, before attaching to the build process in order for it to continue.
This behavior does not exist for the "Release" configuration, so if you just want to run tests or see if a build succeeds, this is best done in the "Release" configuration.
- Constructors
- "With" methods
- Singletons
- INotifyPropertyChanged/INotifyPropertyChanging
- Async overloads
- ToString
- Decorator pattern
This demonstrates the generating of the With
pattern. Simply apply an attribute and the library will do the rest. Remember you are not bound to using fields, but can also use properties and supply the appropriate MemberType
value to the attribute's constructor.
[AllArgsConstructor]
public partial class Person {
private string _name;
private int _age;
}
By supplying the AllArgsConstructor
attribute and making the type partial
, you allow the Source Generator to create a constructor for it containing all of the classes private fields.
If you wish to modify this behavior and would instead like to have a constructor generated off of public properties, you can specify this in the attribute's constructor, e.g.:
[AllArgsConstructor(MemberType.Property, AccessType.Public)]
public partial class Person {
public string Name { get; set; }
public int Age { get; set; }
}
The default is Field
for the MemberType
and Private
for the AccessType
.
It is crucial to make the type partial
, otherwise the Source Generator will not be able to generate a constructor and will throw an exception.
If you only wish to have a constructor generated containing the required fields or properties, Lombok.NET offers the RequiredArgsConstructor
attribute. Fields are required if they are readonly
, properties are required if they don't have a set
accessor.
There is also a NoArgsConstructor
attribute which generates an empty constructor.
For modifying objects after they were created, a common pattern using With...
methods is used. Lombok.NET will generate these methods for you based on members in your class. Here's an example:
[AllArgsConstructor]
[With]
public partial class Person {
private string _name;
private int _age;
}
class Program {
public static void Main() {
var person = new Person("Steve", 22);
person = person.WithName("Collin");
Console.WriteLine(person.Name); // Prints "Collin"
}
}
With methods will only be generated for properties with a setter and fields without the readonly
modifier.
Apply the Singleton
attribute to any partial class and Lombok.NET will generate all the boilerplate code required for making your class a thread-safe, lazy singleton. It will create a property called Instance
in order to access the singleton's instance.
Example:
[Singleton]
public partial class PersonRepository {
}
public class MyClass {
public MyClass() {
var personRepository = PersonRepository.Instance;
}
}
To generate a descriptive ToString
method to your type, make it partial and add the [ToString]
attribute to it. By default, it will include private fields in the ToString
method, but this is customizable in the attribute's constructor.
[ToString]
public partial class Person {
private string _name;
private int _age;
}
When applying this attribute to an enum, Lombok.NET will create an extension class with a ToText
method. This is due to the fact that enums can't be partial, thus an extension method is needed and the extension method will not be found if it is called ToString
.
Generating properties from fields while using them as backing fields is possible using the [Property]
attribute. Example:
public partial class MyViewModel {
[Property]
private int _result;
}
This will create the following property:
public int Result {
get => _result;
set => _result = value;
}
All of the boilerplate code surrounding ÌNotifyPropertyChanged/ÌNotifyPropertyChanging
can be generated using a conjunction of the [NotifyPropertyChanged]
/[NotifyPropertyChanging]
and the [Property]
attributes.
The [NotifyPropertyChanged]
attribute will implement the INotifyPropertyChanged
interface and the PropertyChanged
event. It will also create a method called SetFieldAndRaisePropertyChanged
which sets a backing field and raises the event. The event as well as the method can be used in your ViewModels to implement desired behavior.
If you would like to take it a step further, you can also use the [Property]
attribute on backing fields while passing the PropertyChangeType
parameter to generate properties off of backing fields which will include the raising of the specific event in their setters. Here's an example:
[NotifyPropertyChanged]
public partial class CustomViewModel {
private int _result;
public int Result {
get => _result;
set => SetFieldAndRaisePropertyChanged(out _result, value);
}
// -- OR --
[Property(PropertyChangeType.PropertyChanged)]
private int _result;
}
public class Program {
public static void Main() {
var vm = new CustomViewModel();
vm.PropertyChanged += (sender, args) => Console.WriteLine("A property was changed");
vm.Result = 42;
}
}
If you are using the ReactiveUI library (e.g. when using Avalonia), you can also specify the PropertyChangeType.ReactivePropertyChange
to leverage ReactiveUI's property change handling.
To be able to generate the properties with the property change-raising behavior, the class must have the [NotifyPropertyChanged]
or [NotifyPropertyChanging]
(depending on desired behavior) attribute placed above it.
If you want to have async
overloads for every method in your interface, you can add the [AsyncOverloads]
attribute to it. This also works for abstract classes:
[AsyncOverloads]
public partial interface IRepository<T> {
T GetById(int id);
void Save(T entity);
}
This will add the following methods to your interface:
Task<T> GetByIdAsync(int id);
Task SaveAsync(T entity);
For abstract classes, it will do the same for every abstract method. The inheriting class will be forced to implement the async versions as well. This may also be achieved by using the [Async] attribute.
If you would like to create a simple async
version of your method, you can add the [Async]
attribute to it:
public partial class MyViewModel {
[Async]
public int Square(int i) {
return i * i;
}
}
This will add the following method:
public Task<int> SquareAsync(int i) => Task.FromResult(Square(i));
This works for classes and structs, however it must be partial
.
Lombok.NET also provides an option to generate the boilerplate code when it comes to the decorator pattern. Simply apply the Decorator
attribute to an abstract class or an interface and let the Source Generator do the rest.
[Decorator]
public interface IVehicle {
void Drive();
int GetNumberOfWheels();
}
This will add the following class to your namespace:
public class VehicleDecorator {
private readonly IVehicle _vehicle;
public VehicleDecorator(IVehicle vehicle) {
_vehicle = vehicle;
}
public virtual void Drive() {
_vehicle.Drive();
}
public virtual int GetNumberOfWheels() {
return _vehicle.GetNumberOfWheels();
}
}
Please let me know if there is any other functionality you would like to see in this library. I am happy to add more features.
Planned:
- Switch to Incremental Generators
- Generator which generates immutable
With
methods - [Equals] and [HashCode] generators