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: Partial Everything #9178

Closed
alrz opened this issue Feb 25, 2016 · 11 comments
Closed

Proposal: Partial Everything #9178

alrz opened this issue Feb 25, 2016 · 11 comments

Comments

@alrz
Copy link
Member

alrz commented Feb 25, 2016

Partial Everything

While #5292 is useful for decorating existing members, to actually move the implementation to a partial class definition you might use partial members in the source type declaration. This actually reverses the purpose of the current partial methods and forces the implementation in a partial class declaration, perhaps via #5561.

Partial Methods

Following #6953 it is not required to mark the member as partial on all declarations.

public class Foo {
  public void M() { ... }
}
partial Foo {
  [Attr]
  partial void M();
}

Also access modifiers are "merged" just like for partial class meaning that either it should be the same in every part or it can be omitted in the partial declarations.

Currently C# has the concept of "partial methods" but it's restricted to private methods that returns void and doesn't have out parameters and other modifiers e.g. virtual. The idea is that relax those restrictions to be able to use partial with any method. If the current requirements of partial methods are not satisfied one must implement it in a partial class declaration. For example, if the method is public or it has out parameters or it doesn't return void. In this case, at least one of the partial declarations must implement the method otherwise the compiler produces an error.

Partial Properties

Partial properties are similar to partial methods but they don't actually need to be implemented in case of a auto-implemented property and of course just one of them can have a class-level initializer.

public class Foo {
  public string Name { get; set; }
}
partial class Foo {
  [Attr]
  partial string Name { get; set; }
}

Declarations of getters and setters must be consistent over all parts but just one implementation (if any) is allowed. The other part is generally produced by a code generator. For example (by @HaloFour):

[DependencyObject]
public class Foo {
  public string Name { get; set; }
}

partial class Foo : DependencyObject {
  public static readonly DependencyProperty NameProperty =
    DependencyProperty.Register("Name", typeof(string), typeof(Foo));

  partial string Name {
    get { return (string)GetValue(NameProperty); }
    set { SetValue(NameProperty, value); }
  }
}

Similar rules can be applied to partial events.

@svick
Copy link
Contributor

svick commented Feb 25, 2016

Wouldn't this be a breaking change?

@alrz
Copy link
Member Author

alrz commented Feb 25, 2016

@svick The corner case is partial void M(); which simply can be disambiguated by mentioning the access modifier private indicating that it's an obligatory partial.

@HaloFour
Copy link

How exactly is this related to dependency properties? IIRC the wrapper property is an optional convenience and code generatorso would be able to create them anyway since they're not supposed to do anything but call the GetValue or SetValue methods on the parent instance anyway.

@alrz
Copy link
Member Author

alrz commented Feb 25, 2016

@HaloFour I meant any "rich property" implementation that might think, for example,

partial class Person {
  partial string Name {
    get => GetValue("Name");
    set => SetValue("Name", value); 
  }
}

Can be a possible implementation of the Name property above. This is not addressed by #5292 because it decorates the existing property, meaning that you will need to call original which refers to the auto generated backing field (or another supersedence). In this example we don't need that. This can be an alternative option for ORMs that require every property to be virtual so that they can use reflection to create a proxy and emit overrides to implement lazy loading and change tracking etc.

For dependency/attached properties you can write a code injector (#5561) which turns any partial property to a dependency property and create and initialize required *Property fields.

As a side note, I strongly believe that for #5292 they should consider using decorate instead of replace to be not confused with partials.

@gafter
Copy link
Member

gafter commented Feb 25, 2016

@cston Assigning this to you so you can see if it is related to anything you're working on now.

@HaloFour
Copy link

@alrz

So more like having a designer- or tool-produced partial class define members required to be implemented by the user implemented portion of the partial class?

I don't think that an accessibility modifier will be enough to differentiate between the existing optional partial methods and required partial methods, though.

@alrz
Copy link
Member Author

alrz commented Feb 25, 2016

@HaloFour Implementations are still left to be produced by the code injector. With partial members you actually define your desired members (e.g properties) and let the code injector implement it for you (perhaps, accourding to some attributes or conventions). For example,

// user writes
public partial class MyControl : Control {
  public partial bool Foo { get; set; }
}

// tool generates
partial class MyControl {
  public static readonly DependencyProperty FooProperty = DependencyProperty.Register(...);
  partial bool Foo {
    get => (bool)GetValue(FooProperty);
    set => SetValue(FooProperty, value);
  }
}

You can see that this is not possible to be implemented via #5292 because it requires you to call original in the superseded member.

I don't think that an accessibility modifier will be enough to differentiate between the existing optional partial methods and required partial methods, though.

That of course can be an issue. Although, I don't expect this feature will be ever used with a private method. I presume some restrictions could be applied.

@HaloFour
Copy link

@alrz I see. User defines the skeleton/prototypes of the class, probably decorates it with attributes, tool fits in implementations in a generated partial class:

[DependencyObject]
public partial class Foo {
    public partial string Name { get; set; }
}

And a tool might create:

public partial class Foo : DependencyObject {
    public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Foo));

    public partial string Name {
        get { return (string)GetValue(NameProperty); }
        set { SetValue(NameProperty, value); }
    }
}

@alrz
Copy link
Member Author

alrz commented Feb 26, 2016

I think there is no reason to actually differentiate between required and optional partials. Currently, partial method requirements (private, void, no out params) ensure that they can be safely removed in the call site if they weren't implemented. So, under this proposal, only if any of those conditions were not satisfied, you would need to implement the partial and the compiler produces an error if you didn't.

@alrz
Copy link
Member Author

alrz commented Mar 3, 2016

Following #6953 I think it would be nice to not require partial on every member so,

public class A { [Attr1] public void M() { ... } }
partial class A { [Attr2] partial void M(); }
// produces
public class A { [Attr1, Attr2] public void M() { ... } }

Should be perfectly valid. Also access modifiers are "merged" just like for partial class meaning that either it should be the same in every part or it can be omitted in the partial declarations. But, obviously, only one of them can have a body. Same for partial fields and auto implemented properties (only one of them can have an initializer, useful for applying attributes).

public class A { [Attr1] public int P { get; } }
partial class A { [Attr2] partial int P { get; } }
// produces
public class A { [Attr1, Attr2] public int P { get; } }

@HaloFour's example could be written like this:

[DependencyObject]
public class Foo {
  public string Name { get; set; }
}

partial class Foo : DependencyObject {
  public static readonly DependencyProperty NameProperty = ...;
  partial string Name { ... }
}

It helps to not make class declaration busy with partial keywords all over the place.

@alrz
Copy link
Member Author

alrz commented Dec 31, 2016

Closing in favor of extern methods.

Also, to get rid of additional backing fields generated by auto-properties, the compiler should remove the original declaration if none of replaced members called original.

@alrz alrz closed this as completed Dec 31, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants