Skip to content
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

Proposal: Generic 'Recipes' #2534

Closed
ayesaac opened this issue May 16, 2019 · 2 comments
Closed

Proposal: Generic 'Recipes' #2534

ayesaac opened this issue May 16, 2019 · 2 comments

Comments

@ayesaac
Copy link

ayesaac commented May 16, 2019

Currently, there is no way to require the implementation of any static member on a generic type, with the exception of the default constructor. Given that the default constructor constraint exists, I think it's understood that there is a use case for static calls on generic types.

Now, say you have an interface which is used in a generic context, and you want a non-default constructor on it. You can't do that, but you can create an instance factory method. For example:

public interface IConstructable<T>
    where T : new(), IConstructable<T>
{
    T Build(string foo, int bar);
}

public sealed class ConstructableFactory
{
    public T Build<T>(string foo, int bar)
        where T : new(), IConstructable<T>
    {
        var builderInstance = new T();
        return builderInstance.Build(foo, bar);
    }
}

This is obviously a simple example without much thought behind it, but it illustrates my point. It's messy, inefficient (creating two instances every time you want to create one), and not particularly declarative. A member which doesn't use any instance members should be static.

Now, if we could define static recipes for generics, this would be much nicer. Something like:

public recipe RConstructable
{
    RConstructable(string foo, int bar);
}

And that's it. Much simpler; it's a constructor constructing something, not an instance method calling a constructor without using any instance members, and it's clear that it's always returning a new instance. More readable, more concise and, while the performance difference is unlikely to be relevant but for more extreme situations, more performant than any current option (maybe a lookup for a type -> delegate and a cast? Even less pleasant than the original example, though.).

Recipe's probably a bad name for it, because I don't think you should only be able to define constructors. For example, we could throw in a self (naming's hard) keyword, referring to the type implementing the recipe.

public recipe RParseable
{
    self Parse(string content);

    bool TryParse(string content, out self value);
}

This kind of thing is fairly common in code which uses generics, and there are multiple benefits to this solution.

Recipes could 'inherit' interfaces, too, simplifying common generic method delcarations.

Another solution which might work is just implementing this with current interfaces, and maybe an extra generic keyword. So you could do something like:

public interface IParseable
{
    generic self(string stringRepresentation);

    generic int Foo { get; }

    generic self Parse(string content);

    generic bool TryParse(string content, out self value);
}

generic could imply static, I think; can't think of any reason you'd want to limit a non-static member to be generic-only in an interface. I suspect self would, in this example, only be allowable on generic members.

My immediate thoughts for using this feature are around serialisation and parsing, but I'm sure this would impact plenty of other code.

Code Usage Summary

public interface IInterface
{
    float Foo(float bar);
}

public recipe RRecipe : / * [class, new(),] maybe? */ IInterface
{
    RRecipe(string baz, float qux);

    float Quux { get; }
}

public sealed class RecipeUser
{
    public T Quuz<T>(string bar, float baz, out float corge)
        where T : RRecipe
    {
        var grault = new T(bar, baz);
        corge = grault.Foo(T.Quux);
        return grault;
    }
}
@Joe4evr
Copy link
Contributor

Joe4evr commented May 16, 2019

Sounds like Shapes #164

@ayesaac
Copy link
Author

ayesaac commented May 20, 2019

Looks like shapes will probably cover this

@ayesaac ayesaac closed this as completed May 20, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants