In some cases "ref" is less functional than "in"/"ref readonly" #3419
Replies: 9 comments
-
The most common reason to use a ref parameter, is to reassign it. In this case the reassignment would be invisible, so the use is probably incorrect. Hence why it's not worth doing the same trick as for |
Beta Was this translation helpful? Give feedback.
-
@YairHalberstadt, I agree. But it's not the only reason to use interface IEventLogger {
ValueTask LogAsync
(String eventName,
IEnumerable<(String name, Object? value)> args,
DateTimeOffset timestamp);
}
class EventLogBuilder {
readonly IEventLogger eventLogger;
readonly String eventName;
List<(String name, Object? value)>? args;
DateTimeOffset? timestamp;
public EventLogBuilder (IEventLogger eventLogger, String eventName) {
this.eventLogger = eventLogger;
this.eventName = eventName;
}
public EventLogBuilder With (String name, Object? value) {
args ??= new List<(String, Object?)>(capacity: 4);
args.Add((name, value));
return this;
}
public EventLogBuilder WithTimestamp (DateTimeOffset timestamp) {
this.timestamp = timestamp;
return this;
}
public ValueTask Async () =>
eventLogger.LogAsync(
eventName,
args ?? Enumerable.Empty<(String, Object?)>(),
timestamp ?? DateTimeOffset.Now);
}
static class EventLoggerFunctions {
public static EventLogBuilder Log (this IEventLogger eventLogger, String eventName) =>
new EventLogBuilder(eventLogger, eventName);
}
static class ExampleModule {
public static async void Example (IEventLogger eventLogger) =>
await eventLogger.Log("example-is-presented")
.With("builder-is", "class")
.Async();
} In this example I use interface IEventLogger {
ValueTask LogAsync
(String eventName,
IEnumerable<(String name, Object? value)> args,
DateTimeOffset timestamp);
}
struct EventLogBuilder {
public readonly IEventLogger eventLogger;
public readonly String eventName;
public List<(String name, Object? value)>? args;
public DateTimeOffset? timestamp;
public EventLogBuilder (IEventLogger eventLogger, String eventName) {
this.eventLogger = eventLogger;
this.eventName = eventName;
args = null;
timestamp = null;
}
}
static class EventLoggerFunctions {
public static EventLogBuilder Log (this IEventLogger eventLogger, String eventName) =>
new EventLogBuilder(eventLogger, eventName);
public static ref EventLogBuilder With (this ref EventLogBuilder builder, String name, Object? value) {
builder.args ??= new List<(String, Object?)>(capacity: 4);
builder.args.Add((name, value));
return ref builder;
}
public static ref EventLogBuilder WithTimestamp (this ref EventLogBuilder builder, DateTimeOffset timestamp) {
builder.timestamp = timestamp;
return ref builder;
}
public static ValueTask Async (this ref EventLogBuilder builder) =>
builder.eventLogger.LogAsync(
builder.eventName,
builder.args ?? Enumerable.Empty<(String, Object?)>(),
builder.timestamp ?? DateTimeOffset.Now);
}
static class ExampleModule {
public static async void Example (IEventLogger eventLogger) =>
// ERROR: 'ref this' argument must be an assignable variable, field or an array element.
await eventLogger.Log("example-is-presented")
.With("builder-is", "struct")
.Async();
} Of course, I can just copy structure in every method instead of using I propose to remove that error and set behaviour that
|
Beta Was this translation helpful? Give feedback.
-
Can you run some benchmarks showing this is the case. Also why not make the builder methods instance methods? |
Beta Was this translation helpful? Give feedback.
-
I cannot
I quickly wrote the benchmarks and pubish them on KorneiDontsov/CSharpLangIssue3418Benchmarks. BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.778 (1909/November2018Update/19H2)
Intel Core i7-8750H CPU 2.20GHz (Coffee Lake), 1 CPU, 12 logical and 6 physical cores
.NET Core SDK=3.1.102
[Host] : .NET Core 3.1.2 (CoreCLR 4.700.20.6602, CoreFX 4.700.20.6702), X64 RyuJIT
DefaultJob : .NET Core 3.1.2 (CoreCLR 4.700.20.6602, CoreFX 4.700.20.6702), X64 RyuJIT
|
Beta Was this translation helpful? Give feedback.
-
There's not a huge difference between them. It's highly unlikely that this will be a bottleneck in an application. If it is, you can always assign the builder to a local. It seems to me like the risk of creating a pit of failure, is greater than the potential benefits here. |
Beta Was this translation helpful? Give feedback.
-
@YairHalberstadt, to me it is not. I have no more arguments. Thank you for your time. Feel free to close the issue. |
Beta Was this translation helpful? Give feedback.
-
@KorneiDontsov |
Beta Was this translation helpful? Give feedback.
-
@YairHalberstadt, sorry, I confused "Contributor" with "Collaborator" :) I'm not good at English |
Beta Was this translation helpful? Give feedback.
-
In the case of value types, any instance method call can reassign |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
I ran into a difference in functionality of "ref" and "in"/"ref readonly" that looks awkward to me.
I believe I understand why this error exists, but since compiler does a bit of magic for the "in" parameters, it could do the same for the "ref" parameters, couldn't it?
Beta Was this translation helpful? Give feedback.
All reactions