RecordGenerator icon indicating copy to clipboard operation
RecordGenerator copied to clipboard

Exclude individual properties from the "Wither"

Open bboyle1234 opened this issue 6 years ago • 7 comments

Some immutable objects need to be able to exclude certain properties from the "Withing" feature. Can there be an attribute added to a property that excludes it from the generation of With updaters?

bboyle1234 avatar Sep 20 '19 02:09 bboyle1234

@bboyle1234 I can't think of such a scenario. Why would just having the With available be a problem?

amis92 avatar Sep 20 '19 10:09 amis92

Because as far as I understand the question, everything would be as is, just the selected property's With method wouldn't be generated. Am I right?

amis92 avatar Sep 20 '19 10:09 amis92

Yes, you're right. It's not strictly necessary to exclude the With method for that particular property, but it allows the developer to express intent clearly. Let's take the good old stock market for example, and put together a stock's symbol and price in a record. The developer's intention is that this record be used for updating and distributing the price of a stock. The record is meant to have its price updated, but not the stock symbol. Here's how I'd want to code a record like that:

public sealed class StockPriceUpdate { 
  [SuppressWith] public string Symbol { get; }
  public decimal Price { get; }
}

bboyle1234 avatar Sep 20 '19 11:09 bboyle1234

Currently, it doesn't make sense (the request): there are other ways to instantiate the copy with symbol changed, e.g. constructor, builder, Update method. A comment should pass the intent "enough".

In this scenario, the class isn't really a record: It has some constraints that disqualify it, like disallowing mutation of some property. You'd also probably want to hide the constructor as internal, disable Builder, hide the Update. Overall, it seems like too much work to even use the Record.

In terms of benefitting from Equality etc. (I suppose it's a feature that could make this customization effort worth marking this class as Record), I start to think that we should build a palette of different generators, and equality/comparisons are great candidates to separate them out.

amis92 avatar Sep 20 '19 12:09 amis92

Yes. Like everyone, I'm very much looking forward to the legendary c# 8.0 records feature. But not because I want records. It's because I want to work with immutability and memberwise equality without too much boiler plate code. This project, though it's named "record" and attempts to be records, is actually the best library I have found for what I (and all the other developers) really need -- a proper library for efficiently implementing immutability and memberwise equality.

It's very tempting to limit the scope of this project to "just records" because that's a lot narrower and simpler. But what we all really need is a library that allows us to write flexible immutable classes.

bboyle1234 avatar Sep 20 '19 12:09 bboyle1234

It would be really cool if this project would allow setting the publicity of the builders, constructors, etc to something other than public.

I've seen this done in AArnott's ImmutableObjectGraph library.

Other things I really liked from that library were the ability to pre-calculate the hashcode just once, in the constructor, and I especially like the convention of using readonly fields instead of readonly properties.

That's because when writing immutable objects, we often don't want to expose all the readonly fields as public properties, or we want to expose properties that are formed differently from the underlying readonly fields.

bboyle1234 avatar Sep 20 '19 12:09 bboyle1234

It would be really cool if this project would allow setting the publicity of the builders, constructors, etc to something other than public. I've seen this done in AArnott's ImmutableObjectGraph library.

I'm lazy. Can you summarize how it was done?

Other things I really liked from that library were the ability to pre-calculate the hashcode just once, in the constructor, and I especially like the convention of using readonly fields instead of readonly properties.

I'm a strong supporter of properties, because they offer a much better extensibility approach, are more concise, were designed for publicly exposing "data" members (not methods) and have superior tooling support in a staggering number of scenarios. Fields are much simpler indeed. That'd be their only advantage.

That's because when writing immutable objects, we often don't want to expose all the readonly fields as public properties, or we want to expose properties that are formed differently from the underlying readonly fields.

That might be true, but in that case - most of this library's features are useless. What's left? Constructor? How would Builders or Withers work if there's no ctor params <-> backing fields <-> properties parity? Equality is a recent addition, and I'm more and more convinced it should be extracted into a separate library. It's undeniably useful, and in more scenarios than records are. "Primary" constructor would be another candidate, so it'd fulfill the need for a more customizable feature set, like accessibility, additional extension points, readonly fields support, etc.

amis92 avatar Sep 20 '19 13:09 amis92