Home > Mobile >  Is it possible to constrain a method for records only?
Is it possible to constrain a method for records only?

Time:11-20

Let's assume I have several different record-types with a common interface, as follows:

record Rec1(
    string Tag
    ) : IFoo;

record Rec2(
    int Num,
    string Tag
    ) : IFoo;

record Rec3(
    bool Flag
    ) : IFoo
{
    public string Tag { get; init; }
    public double Value { get; init; }
}

// ... others ...

interface IFoo
{
    string Tag { get; }
}

My goal is to write a function to update the Tag property via record clonation.

Now, if the type were known, I could write something like this:

    private Rec2 Update(Rec2 rec, string tag)
    {
        return rec with { Tag = tag };
    }

However, I would avoid to write a bunch of overloads, so I'm wondering if there's a more generic way to achieve that. For instance, I'd tempted to write as:

    private TRec Update<TRec>(TRec rec, string tag) where TRec : IFoo
    {
        return rec with { Tag = tag };
    }

but that won't compile, because TRec could be anything (class, struct, etc), not just a record where I can use the with statement. By the way, there's no a record-constraint I could apply.

So, is there any workaround? is it anything to expect to be solved in the future?

As a bonus question (just for sake of curiosity), why a record-translated class isn't marked with a special interface, in order to make the with statement working against?

CodePudding user response:

Instead of having an empty base record, have all these records inherit from a shared base record, that implements your interface:

record R : IFoo ...

record Rec1 : R....
record Rec2 : R....

private TRec Update<TRec>(TRec rec, string tag) where TRec : R
{
    return rec with { Tag = tag };
}

CodePudding user response:

Thanks to another answer (see link in the comments), it seems to be a hacky workaround:

record R();

record Rec1(
    string Tag
    ) : R, IFoo;

// ... others ...

interface IFoo
{
    string Tag { get; init; }
}

private TRec Update<TRec>(TRec rec, string tag) where TRec : R, IFoo
{
    return rec with { Tag = tag };
}

The constraint is effective due the R record, while the Tag assignment thanks to the init accessor.

Again, to me is much like a hack than a real solution. I'd prefer to see some native support.

  • Related