When using the C# with
keyword with an actual struct type it works exactly as expected, however when dealing with some generic type T
that is constrained to be a struct
it results in a compiler error.
Is this a bug or intended behaviour, and if so, what's stopping the generic approach from working?
public interface ICountSomething
{
public int Counter { get; }
}
public struct ActualStruct : ICountSomething
{
public string Name { get; init; }
public int Counter { get; init; }
}
public class Blah<T>
where T : struct, ICountSomething
{
public T DoSomethingWithGenericConstrainedToStruct(T input)
{
// Does not compile
return input with
{
Counter = input.Counter 1,
};
}
public ActualStruct DoSomethingWithActualStruct(ActualStruct input)
{
// Compiles ok
return input with
{
Counter = input.Counter 1,
};
}
}
CodePudding user response:
There are two issues - Counter
on the interface is readonly so can't be modified and/or used inside with
expression. Change the interface to:
public interface ICountSomething
{
public int Counter { get; init; }
}
The with
itself seems to be Rider/Resharper issue, the code should compile just fine (after adding init
to the interface) - checked at sharplab.io. Submitted the bug for Rider.
CodePudding user response:
Since T
is a generic type, the compiler does not know which type is going to be used for T.
It knows it is a struct, but it cannot tell which properties struct T
has.
The with
keyword has to know which properties it can set. Since there is no way the compiler can possibly know which properties there are in T
, you cannot use with
.
You also can't use an interface here because there is not a specific interface for value or record types.
Edit: this works: notice the init
on the property of the interface.
public interface ICountSomething
{
public int Counter { get; init; }
}
public struct ActualStruct : ICountSomething
{
public string Name { get; init; }
public int Counter { get; init; }
}
public class Blah<T>
where T : struct, ICountSomething
{
public T DoSomethingWithGenericConstrainedToStruct(T input)
{
// Compiles ok
return input with
{
Counter = input.Counter 1,
};
}
public ActualStruct DoSomethingWithActualStruct(ActualStruct input)
{
// Compiles ok
return input with
{
Counter = input.Counter 1,
};
}
}