-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Discussion: Code Generator Catalog #16160
Comments
I get where you're going with |
@HaloFour Only if you don't mind the additional backing field per property (or having to return |
The Either way, I don't disagree with the concept, just mentioning the additional complexity it creates. Order of member replacement is already a known concern. |
The compiler should remove the original declaration if none of /cc @HaloFour I've updated OP to reflect this. |
One could implement [GenerateDisposable]
public partial class Resource : IDisposable
{
[Dispose]
private IntPtr nativeResource;
[Dispose]
private AnotherResource managedResource;
...
} Generating [RewriteAsync]
void SendClosureAlert()
{
_buf[0] = (byte)ContentType.Alert;
Utils.WriteUInt16(_buf, 1, (ushort)_connState.TlsVersion);
_buf[5 + _connState.IvLen] = (byte)AlertLevel.Warning;
_buf[5 + _connState.IvLen + 1] = (byte)AlertDescription.CloseNotify;
int endPos = Encrypt(0, 2);
_baseStream.Write(_buf, 0, endPos);
_baseStream.Flush();
}
//generated:
async Task SendClosureAlertAsync(CancellationToken cancellationToken)
{
_buf[0] = (byte)ContentType.Alert;
Utils.WriteUInt16(_buf, 1, (ushort)_connState.TlsVersion);
_buf[5 + _connState.IvLen] = (byte)AlertLevel.Warning;
_buf[5 + _connState.IvLen + 1] = (byte)AlertDescription.CloseNotify;
int endPos = Encrypt(0, 2);
await _baseStream.WriteAsync(_buf, 0, endPos, cancellationToken);
await _baseStream.FlushAsync(cancellationToken);
} Allocation free public partial class MyFib
{
public partial struct FibIter {}
[Iterator(typeof(FibIter), "Fib", BindingFlags.Public)]
private IEnumerable<int> GenFib()
{
int j = 0, i = 1;
while(true)
{
yield return i;
j += i;
yield return j;
i += j;
}
}
}
// generated
public partial class MyFib
{
public FibIter Fib()
{
return default(FibIter);
}
public partial struct FibIter: IEnumerable<int>, IEnumerator<int>
{
private int _1, _2, j, i;
public FibIter GetEnumerator() => this;
public int Current => _2;
public bool MoveNext()
{
switch (_1)
{
case 0:
this._1 = -1;
this.j = 0;
this.i = 1;
break;
case 1:
this._1 = -1;
this.j += this.i;
this._2 = this.j;
this._1 = 2;
return true;
case 2:
this._1 = -1;
this.i += this.j;
break;
default:
return false;
}
this._2 = this.i;
this._1 = 1;
return true;
}
object IEnumerator.Current => _2;
void IDisposable.Dispose() {}
void IEnumerator.Reset()=> throw new NotSupportedException();
IEnumerator<int> IEnumerable<int>.GetEnumerator() => this;
IEnumerator IEnumerable.GetEnumerator() => this;
}
} A very similar thing could be done to implement async differently and save allocations, for example #15491 (comment) |
RecordsThis is a temporary solution until Records are released, but, https://github.com/ufcpp/RecordConstructorGenerator Type aliases/Strong typedef[Alias]
struct Name { string value; }
[Alias]
struct Distance { double value; }
// generated
partial struct Name
{
public string Value => value;
public Name(string value) { this.value = value; }
// equality, cast operator, etc.
}
partial struct Distance
{
public double Value => value;
public Distance(double value) { this.value = value; }
// equality, cast operator, etc.
} MixinsFor mixin use cases, I want an ability to pass public class Sample
{
NotifyPropertyChangedMixin _npc;
}
// generated
public class Sample : INotifyPropertyChanged
{
NotifyPropertyChangedMixin _npc;
public event PropertyChangedEventHandler PropertyChanged { add { _npc.PropertyChanged += value; } remove { _npc.PropertyChanged -= value; } }
protected void OnPropertyChanged(PropertyChangedEventArgs args) => _npc.OnPropertyChanged(this, args);
protected void OnPropertyChanged([CallerMemberName] string propertyName = null) => _npc.OnPropertyChanged(this, propertyName);
protected void SetProperty<T>(ref T storage, T value, PropertyChangedEventArgs args) => _npc.SetProperty(this, ref storage, value, args);
protected void SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null) => _npc.SetProperty(this, ref storage, value, propertyName);
} |
Personally I would love having a generator write serialization functions for my objects. How would one go about debugging such generated code tho? Manipulating code-text directly might not be the best approach though, so we would end up manipulating the Ast? So this feature would most likely be some kind of more thightly integrated T4 system (or a variant using the AST)? This could also be utilized in some obfuscators maybe... Thoughts? |
@asdfgasdfsafgsdfa You can refer to the original proposal here: #5561. It would not manipulate AST or anything, it is a "code generator" so it can only add code to the compilation unit. Debugging works directly on the generated code. |
@alrz I would rather see replacement instead of (or in addition to) addition of code and AST modification instead of manipulating code-strings. Should I focus on #5561 then? Oh and another quick question: Has there been any official opinion on those questions? I saw it on a "strong interest" list somewhere, but as I understand the actual implementation and usage is still pretty unclear and heavily discussed. Have any of the people that will decide in the end mentioned their own opinion on those more detailed questions? |
One other possible use of Generators could be for bitfield structs this implementation used reflection and so was 10'000 times slower than using bitmasks (!) but using Generators the conversion functions to ToUInt64() and ToBinaryString() could be generated at runtime: https://www.codeproject.com/Articles/1095576/Bit-Field-in-Csharp-using-struct |
Another thing that I thought about is using it in the following manner:
|
F# community is discussing a type provider to generate types from types. Felt kind of similar. This comment has some probable usage- fsharp/fslang-design#125 (comment) |
Generate PInvoke Code |
|
I want to add lightweight(local-only) Actor models like nAct and this one to the list- https://github.com/David-Desmaisons/EasyActor |
Issue moved to dotnet/csharplang #341 via ZenHub |
Here is an incomplete list of potential use cases for code generators to explore ideas and discussion. I believe this will help to shape generator APIs and language features around it.
1. NPC implementation (huh)
Also, if a property depends on others, we could also raise additional events by inspecting its accessor.
Note, #850 can lead to simpler code generation i.e does not need to figure out a name for backing field:
Or the type (#8364):
Note: If none of
replace
d members calloriginal
the original declaration should be removed as the backing fields are no longer being used.2. Dependency Injection
Dependency containers commonly depend on reflection to create objects. This could be moved to compile-time via code generators. In this scenario, there should be a way to parametrize the code generator to switch between implementations. e.g. mocks. (attributes does not belong to MEF).
Note that this will be only useful to manage object lifecycle. If implementations come from outside of assembly boundaries we should probably fallback to reflection under the hood.
3. Caching / Lazy Initialization
4. Memoization
5. Dependency Properties
Note, it would be nice to be able to inspect property initializer and use it in the generated code, e.g.
6. ORMs
Currently ORMs) use runtime code generation (NH) or proxies (EF) to enable change tracking and lazy loading in POCOs. They could ship with a code generator to move this procedure to the compile-time.
7. Mixins
It's possible to implement member delegation as an analyzer but if we want to delegate all members, perhaps a code generator is more likely preferable.
8. Double Dispatch
This could be used to implement visitor pattern or a simple double dispatch:
You could use a similar technique to generate semi-virtual extension methods. Since this is generated by a generator, you are free to handle the failure case differently.
Generators should be able to produce diagnostics if target members are malformed, e.g. a method instead of a property.
9. Duck Typing
Though, #11159 + #258 = #8127 can greatly improve this scenario in terms of perf and easier code gen.
Note: An assembly attribute could be used to annotate exterior types,
10. Type providers for xml, json, csv, sql, etc
F# type providers can take a string as parameter to bootstrap code generation. This requires code generators to accept a parameter which is not possible in #5561.
11. Variadic generics
There are types with variable arities including Func, Action and ValueTuple. It is a common scenario where we want to have a bunch of similar methods that are only different in number of generic types.
12. Basic implementation
Some of interfaces like
IEquitable
,IComparable
etc, given "key properties" could be implemented via generators . This can also be implemented as an analyzer, but with generators it would be totally transparent.ToString
overrides also belong to this category.13. Serialization
Serialization to various formats like json, can be provided at compile-time without using reflection.
The text was updated successfully, but these errors were encountered: