Coder Social home page Coder Social logo

csharplang's Introduction

C# Language Design

Join the chat at https://gitter.im/dotnet/csharplang Chat on Discord

Welcome to the official repo for C# language design. This is where new C# language features are developed, adopted and specified.

C# is designed by the C# Language Design Team (LDT) in close coordination with the Roslyn project, which implements the language.

You can find:

If you discover bugs or deficiencies in the above, please leave an issue to raise them, or even better: a pull request to fix them.

For new feature proposals, however, please raise them for discussion, and only submit a proposal as a pull request if invited to do so by a member of the Language Design Team (a "champion").

Discussions

Debate pertaining to language features takes place in the form of Discussions in this repo.

If you want to suggest a feature, discuss current design notes or proposals, etc., please open a new Discussion topic.

Discussions that are short and stay on topic are much more likely to be read. If you leave comment number fifty, chances are that only a few people will read it. To make discussions easier to navigate and benefit from, please observe a few rules of thumb:

  • Discussion should be relevant to C# language design. If they are not, they will be summarily closed.
  • Choose a descriptive topic that clearly communicates the scope of discussion.
  • Stick to the topic of the discussion. If a comment is tangential, or goes into detail on a subtopic, start a new discussion and link back.
  • Is your comment useful for others to read, or can it be adequately expressed with an emoji reaction to an existing comment?

Language proposals which prevent specific syntax from occurring can be achieved with a Roslyn analyzer. Proposals that only make existing syntax optionally illegal will be rejected by the language design committee to prevent increased language complexity.

Proposals

Once you have a fully fleshed out proposal describing a new language feature in syntactic and semantic detail, please open an issue for it, and it will be labeled as a Proposal. The comment thread on the issue can be used to hash out or briefly discuss details of the proposal, as well as pros and cons of adopting it into C#. If an issue does not meet the bar of being a full proposal, we may move it to a discussion, so that it can be "baked" further. Specific open issues or more expansive discussion with a proposal will often warrant opening a side discussion rather than cluttering the comment section on the issue.

When a member of the C# LDM finds that a proposal merits discussion, they can Champion it, which means that they will bring it to the C# Language Design Meeting. If the LDM decides to work on adopting the feature, the proposer, the champion and others can collaborate on adding it as a document to the Proposals folder, where its evolution can be tracked over time.

Design Process

Proposals evolve as a result of decisions in Language Design Meetings, which are informed by discussions, experiments, and offline design work.

In many cases it will be necessary to implement and share a prototype of a feature in order to land on the right design, and ultimately decide whether to adopt the feature. Prototypes help discover both implementation and usability issues of a feature. A prototype should be implemented in a fork of the Roslyn repo and meet the following bar:

  • Parsing (if applicable) should be resilient to experimentation: typing should not cause crashes.
  • Include minimal tests demonstrating the feature at work end-to-end.
  • Include minimal IDE support (keyword coloring, formatting, completion).

Once approved, a feature should be fully implemented in Roslyn, and fully specified in the language specification, whereupon the proposal is moved into the appropriate folder for a completed feature, e.g. C# 7.1 proposals.

DISCLAIMER: An active proposal is under active consideration for inclusion into a future version of the C# programming language but is not in any way guaranteed to ultimately be included in the next or any version of the language. A proposal may be postponed or rejected at any time during any phase of the above process based on feedback from the design team, community, code reviewers, or testing.

Milestones

We have a few different milestones for issues on the repo:

  • Working Set is the set of championed proposals that are currently being actively worked on. Not everything in this milestone will make the next version of C#, but it will get design time during the upcoming release.
  • Backlog is the set of championed proposals that have been triaged, but are not being actively worked on. While discussion and ideas from the community are welcomed on these proposals, the cost of the design work and implementation review on these features are too high for us to consider community implementation until we are ready for it.
  • Any Time is the set of championed proposals that have been triaged, but are not being actively worked on and are open to community implementation. Issues in this can be in one of 2 states: needs approved specification, and needs implementation. Those that need a specification still need to be presented during LDM for approval of the spec, but we are willing to take the time to do so at our earliest convenience.
  • Likely Never is the set of proposals that the LDM has rejected from the language. Without strong need or community feedback, these proposals will not be considered in the future.
  • Numbered milestones are the set of features that have been implemented for that particular language version. For closed milestones, these are the set of things that shipped with that release. For open milestones, features can be potentially pulled later if we discover compatibility or other issues as we near release.

Language Design Meetings

Language Design Meetings (LDMs) are held by the LDT and occasional invited guests, and are documented in Design Meeting Notes in the meetings folder, organized in folders by year. The lifetime of a design meeting note is described in meetings/README.md. LDMs are where decisions about future C# versions are made, including which proposals to work on, how to evolve the proposals, and whether and when to adopt them.

Language Specification

It is our plan to move the C# Language Specification into Markdown, and draft it in the spec folder. The spec drafts will eventually be standardized and published by ECMA. The folder currently contains an unofficial Markdown version of the C# 6.0 specification for convenience.

Implementation

The reference implementation of the C# language can be found in the Roslyn repository. This repository also tracks the implementation status for language features. Until recently, that was also where language design artifacts were tracked. Please allow a little time as we move over active proposals.

csharplang's People

Contributors

333fred avatar agocke avatar alekseyts avatar alrz avatar billwagner avatar cston avatar cyrusnajmabadi avatar eltociear avatar eyalsk avatar gafter avatar gewarren avatar jaredpar avatar jcouv avatar jjonescz avatar jnm2 avatar kathleendollard avatar leandromoh avatar madstorgersen avatar mairaw avatar nschonni avatar omartawfik avatar pkulikov avatar rekkonnect avatar rikkigibson avatar stephentoub avatar svick avatar tannergooding avatar vsadov avatar yairhalberstadt avatar youssef1313 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

csharplang's Issues

Spec is missing script syntax

Reference directives, load directives and other differences from the regular syntax (e.g. interleaved type definitions, usings, extension methods, and the generated entry point).

Proposal: by-ref struct operator parameters

Related to: dotnet/roslyn#165, dotnet/roslyn#115

One substantial performance gap between C# and C++ is C#'s high overhead of using struct operators, because of their pass by value copying semantics. In a simple overhead test for a relatively small vect3d type (vector of 3 doubles), the cost of pass-by-value is 3x vs implementing the same function with ref arguments. As the struct types become larger, the overhead of pass-by-value grows even higher.

public struct vect3d
{
    public double x, y, z;
    public static vect3d operator +(vect3d a, vect3d b)
    {
        a.x += b.x;
        a.y += b.y;
        a.z += b.z;
        return a;
    }
}

While C# offers many advantages over C++, software which spends a significant amount of time performing mathematical operations on struct datatypes is taxed by their performance overhead. Using non-operator functions is not a practical solution, as mathematical code becomes too hard to read.

One possibility is to combine by-ref operators with dotnet/roslyn#115 (readonly parameters), to enable higher performance struct operator implementations while enforcing (or encouraging) immutable value-type semantics for the caller.

public struct vect3d
{
    public double x, y, z;
    public static vect3d operator +(readonly ref vect3d a, readonly ref vect3d b)
    {
        vect3d r;
        r.x = a.x + b.x;
        r.y = a.y + b.y;
        r.z = a.z + b.z;
        return r;
    }
}

If readonly is merely a construct of the C# compiler, then the parameters are not immutable WRT to CLR. This is analogous to the const references used in C++ operators, which are not strictly enforced. In practice, C++ operators are generally well behaved in respecting immutable intent of these const arguments, and I believe C# "readonly ref" operator overloads would also be well behaved - while achieving significant performance improvement from avoiding copies.

It may be advantageous to allow value-copy and ref overloads of the same operator, to maintain backward compatibility with already compiled code. For new resolutions, the compiler would prefer the ref version of the operator overload. One option would be for the compiler to only allow a single matching overload (either readonly-ref or non-ref), and in the case of "readonly ref" it could generate the non-ref version for compatibility.

If the CLR and verifier are taught about "readonly ref", and they are allowed more generally in function parameters, the compiler could allow the user to omit the "ref" decorator for any "readonly ref" parameter in a function invocation, since "readonly" assures that parameter will maintain value type semantics. In this case, it might be advantageous to look for alternative syntax options, to avoid a noisy proliferation of "readonly ref" on many value-type parameter declarations. One option is to make non-async struct parameters pass as "readonly ref" by default. However, for very small structs (like IntPtr) pass by reference is marginally slower than pass by value. (20% slower in one trivial test) Another option is to use a shorter keyword, such as "in", matching up "in/out/ref".

Proposal: Single Function Classes

In pursuit of "single responsibility principle", i do break the code down to small classes, which in some cases end up having a single method/function. Here is an example:

internal class FieldTypeService(SomeDependency dependency)
{
  public Type DetermineFieldType(string type, bool isNullable)
  {  
   ... // implement 
  }
}

So the class is almost a C-style function, but it has dependencies, which enables isolated testing, unlike actual C-function, which calls other functions freely and uncontrolled.
The nuisance here is that i have to come up with 2 names, one for class name, and one for method name.
Here is the suggestion i have for the syntax:

internal class DetermineFieldType(SomeDependency dependency)(string type, bool isNullable)
{
... // implement
}

Suggested syntax for using such a class as a dependency:

internal class MyClass(DetermineFieldType determineFieldType)
{
  void SomeMethod()
  {
    determineFieldType("someType", false);
  }
}

or, if i am to create an instance manually (which i never do, but anyway):

var output = new DetermineFieldType(new SomeDependency())("someType", false);

Compiler would transform a body of a class into a method named "Invoke" or something like that.

What i could not come up with yet is where to put readonly fields in this syntax. Maybe it could go without any fields at all, except those that come from primary constructor.

Convert delegate types with compatible signature automatically

.NET has inherited a whole sea of delegate types from .NET 1.0. Thanks to Func and Action they are largely obsolete now. C# should allow implicit conversions between delegate types of compatible signature.

See https://lostechies.com/jimmybogard/2008/03/26/stop-creating-custom-delegate-types/

public delegate bool CustomMatchingFunction(string value);

public void Custom_delegates_are_bad()
{
    Predicate<string> match1 = value => value == "Blarg";
    Func<string, bool> match2 = value => value == "Blarg";
    CustomMatchingFunction match3 = value => value == "Blarg";

    match1 = match2;
    match1 = (Predicate<string>)match2;
    match1 = match3;
}

All of these assignments should just work. If there are objections to making this an implicit conversion an explicit one would work as well.

Champion "Async Streams" (including async disposable) (16.3, Core 3)

See dev spec: https://github.com/dotnet/roslyn/blob/main/docs/features/async-streams.md
See also dotnet/roslyn#261

LDM notes:

Proposal discussions

Are discussions about proposals supposed to only happen in the mailing list or can they also be entered as issues in the repo?

The only concerns about them happening only in the mailing list is that they're not readily visible/accessible and aren't part of the repo (so no history, searching, etc. that we'd get with having them as issues).

Also, how long should it typically take to receive the confirmation email after subscribing to the mailing lists?

Champion "Nullable-enhanced common type"

Summary

There is a situation in which the current common-type algorithm results are counter-intuitive, and results in the programmer adding what feels like a redundant cast to the code. With this change, an expression such as condition ? 1 : null would result in a value of type int?.

This and #881 should be taken into the language at the same time.

Champion "default interface methods" (16.3, Core 3)

Open issues are being tracked at #406

LDM history:

Champion "Expression variables in initializers" (15.7)

See also dotnet/roslyn#16270

There are three contexts where out variable declarations and pattern matching variable declarations are not permitted today, in part because we did not decide what the scope should be. I propose we decide for each of these three contexts, and extend the language to permit declaration expressions in these contexts.

  1. Constructor-initializers. The primary question is whether the scope of variables declared in a ctor-initializer extends to the constructor body or not. I propose that it should extend to the body.

  2. Field initializers. The primary question is whether the scope of variables declared in the one equals-value-clause is in scope in subsequent equals-value-clauses of the same field declaration (when it declares more than one field). It is also possible to extend the scope to subsequent separate field declaration's initializers too, but I don't think that is likely to be approved by the LDM.

  3. Query clauses. The primary question is what is the scope of variables declared in a query clause that is specified to be translated into the body of a lambda. One possible resolution is that its scope is just that expression of the clause.

Champion "Nullable reference types" (16.3, Core 3)

Note this includes the object constraint: where T : object.

LDM:

Proposal: add an F-bound or CRTP (CRGP?) constraint for generic type parameters

Problem:

An abstract class, let's name BaseClass, is defined that contains a type parameter, let's name TSubClass, intended to represent the future subclass version of the BaseClass<TSubClass> itself. Currently, we can constraint TSubClass to BaseClass<TSubClass> using where TSubClass : BaseClass<TSubClass> and ensure that TSubClass is itself a subclass of BaseClass. However, we cannot guarantee that TSubClass is the subclass of BaseClass<TSubClass> that is deriving BaseClass<TSubClass>. This makes it risky to assume that any given instance of a subclass of BaseClass<TSubClass> is actually TSubClass within any member of BaseClass<TSubClass>. Here is an example illustrating the point:

public class BaseClass<TSubClass> where TSubClass : BaseClass<TSubClass>
{
    public TSubClass DoSomething()
    {
        /* do something interesting */
        return this; // <- currently results in 'error CS0266: Cannot implicitly 
                     // convert type 'BaseClass<TSubClass>' to 'TSubClass'. An 
                     // explicit conversion exists (are you missing a cast?)' 
    }
}

public class SubClass1 : BaseClass<SubClass1>
{
    public SubClass1 DoSomethingSpecific()
    {
        /* do something interesting */
        return this;
    }
}

public class SubClass2 : BaseClass<SubClass1>
{
    public SubClass2 DoSomethingElseSpecific()
    {
        /* do something interesting */
        return this;
    }
}

The first obvious usage for being able to implicity return TSubClass reliably is for the construction of fluent interface classes that aggregate information where the sub classes are expected to expand the language. Of course, there are other uses like where BaseClass<TSubClass> might attempt to pass itself as an argument to another method where the parameter is typed to TSubClass with the same constraint where TSubClass : BaseClass<TSubClass>. The only semi safe option in these cases is to validate that this is a type of TSubClass and throw a runtime exception if it is not. It would be far more ideal if we could know this at compile time. Logically, there should be nothing to stop the compiler from being able to ensure this safety with a proper constraint. Which leads me to...

A proposal for a new type constraint:

Introduce a new generic type constraint to enforce the following compile time checks:

  1. Enforce that the constrained type parameter must subclass the class where the type parameter is defined (the same as specifying TSubClass : BaseClass<TSubClass> [note that this is redundant with check 3, but I'm including it to associate this specialized constraint with the already existing subclass constraint type];
  2. Enforce that the class where the type parameter is defined must be marked abstract as the constraint would not make sense on a concrete or sealed class;
  3. Enforce either...
    • ... that the type argument for the constrained type parameter must be the class type that is deriving the BaseClass and not some other class type that has already derived the BaseClass allowing the compiler to either assign the constrained type to this instead of the abstract form or validate that this implicitly converts to the constrained type;
    • ... or that the type argument for the constrained type parameter must also be a type parameter on the subclass that is constrained with the same new type constraint and enforce the same checks upon the subclass type parameter;

As a suggestion for the keyword for the new type constraint might I offer up the following three possibilities:

  1. this; example usage: BaseClass<TSubClass> where TSubClass : this {}
  2. subclassed; example usage: BaseClass<TSubClass> where TSubClass : subclassed {}
  3. concrete; example usage: BaseClass<TSubClass> where TSubClass : concrete {}

With this new constraint the following would be possible:

// Demonstrates an invalid declared use of the new proposed type constraint (using
// 'this' as the constraint keyword) on a non-compliant base class that is not marked
// abstract
public class NonAbstractBaseClass<TSubClass>
    where TSubClass : this // <- would result in error CS????: The type
    // 'NonAbstractBaseClass<TSubClass>' must be marked 
    // abstract in order to use the 'this' type constraint on a 
    // type argument defined on the class.
{
}

// Demonstrates a valid declared use of the new proposed type constraint (using 'this'
// as the constraint keyword)
public abstract class BaseClass<TSubClass>
    where TSubClass : this
{
    public TSubClass DoSomething()
    {
        /* do something interesting */
        return this; // <- would no longer throw a compilation error
    }
}

// Demonstrates a class that provides a legal type argument for the 'this'
// constrained type parameter
public class SubClass1 : BaseClass<SubClass1>
{
    public SubClass1 DoSomethingSpecific()
    {
        /* do something interesting */
        return this;
    }
}

// Demonstrates a class that does not provide a legal type argument for the 'this'
// constrained type parameter
public class SubClass2 : BaseClass<SubClass1> // <- would result in error CS0311: The 
    // type 'SubClass1' cannot be used as type 
    // parameter 'TSubClass' in the generic 
    // type or method 'BaseClass<TSubClass>'.
    // There is no implicit reference 
    // conversion from 'SubClass2' to 
    // 'SubClass1'
{
    public SubClass2 DoSomethingElseSpecific()
    {
        /* do something interesting */
        return this;
    }
}

// Demonstrates an intermediate class that provides a legal type argument for the
// 'this' constrained type parameter by supplying a type parameter of its class that
// is also constrained with the 'this' type constraint
public abstract class IntermediateClass<TSubClass> : BaseClass<TSubClass>
    where TSubClass : this
    {
        public TSubClass DoSomethingIntermediateLike()
        {
            /* do something interesting */
            return this;
        }
    }

// Demonstrates a class that provides a legal type argument for the 'this'
// constrained type parameter to an intermediate abstract generic class that
// passes the type parameter as a type argument to another 'this' constrained
// type parameter of an abstract generic class
public class SubClass3 : IntermediateClass<SubClass3>
{
    public SubClass3 DoSomethingEvenMoreSpecific()
    {
        /* do something interesting */
        return this;
    }
}

// Demonstrates instantiation of the legally defined classes, SubClass1 and
// SubClass3 and verifies that the type resolution of the return parameters works
// correctly in support of the definition of fluent interface like methods on the
// generic classes.
public class TestSubClasses
{
    public void TestThem()
    {
        var subClass1 = new SubClass1();
        var subClass3 = new SubClass3();

        subClass1.DoSomething()
            .DoSomethingSpecific();

        subClass3.DoSomething()
            .DoSomethingIntermediateLike()
            .DoSomethingEvenMoreSpecific();

    }
}

Note that since all of the checks would be handled at compile time both for the definition of the abstract generic class with the constrained generic type parameter and for the definition of the consuming subclass providing the constraint checked generic type argument, I don't see a reason why the CLR would need to be updated to support this new constraint type. All uses of 'this' within the abstract class would be checked by the compiler and which could emit the proper IL. Enforcing that the subclass must be abstract, and that the type argument must be the subclass that is being defined or another constrained type parameter on the subclass could also be checked by the compiler.

Proposal: support "type of the current object" as declared return type.

public class Self
{
    public this Self1()
    {
        // Only allowed to return with keyword this.
        // Very important that only 'this' is allowed as returning value.
        return this;
    }

    public this Self2()
    {
        // NOT allowed.
        return new Self();
    }

    public this Self3()
    {
        // NOT allowed.
        return new Object();
    }
}

Spec isn't Versioned

There should be a way to view the spec for each version. Tags or folders I'm thinking

[Proposal] Forcing ConfigureAwait(false) on assembly level

Problem

It's recommended to always use ConfigureAwait(false) in certain kinds of libraries. While it's definitely possible to use an analyzer (e.g. ConfigureAwaitChecker.Analyzer) to catch those cases, the analyzer has to be installed separately and the resulting code is awkward and verbose.

Potential solutions

Option A

Provide an assembly-level attribute that would force compiler to generate (pattern-based) ConfigureAwait(false) calls for each await.

Option B
  1. Implement CallerMethodAttribute from #351
  2. Add support for method info attributes to pattern-based GetAwaiter calls
  3. This would allow for a new overload GetAwaiter([CallerMethod] MethodBase method = null)
  4. Task could use this overload to look for some attribute on method.Assembly and return a correspondingly preconfigured awaiter.

`using` declaration on any block

Example:

class MyClass {
   using Foo;

   void MyMethod() {
      using Bar;

      // Foo and Bar visible

      using Baz;

      // Foo, Bar and Baz visible
   }
}

Nullable reference types: Unconstrained generic causes error CS0453

I have been playing with nullable reference types, which is already working remarkably great! To try it out, I have patched up my analyzer to convert Resharper's (Item)NotNull/CanBeNullAttribute annotations to nullable reference types. Then ran that on an actual annotated codebase.

During that process I discovered some oddities, which I'll create issues for here.

Version Used: Built from source, branch NullableReferenceTypes (commit dotnet/roslyn@8553cda)

Steps to Reproduce:
When changing the type of parameter value in the next code block to T?, error CS0453 is raised:

Error CS0453: The type 'T' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Nullable<T>'

    public static class Guard
    {
        public static void NotNull<T>(T value, string name)
        {
            if (ReferenceEquals(value, null))
            {
                throw new ArgumentNullException(name);
            }
        }
    }

Expected Behavior: No error, allowing callers to pass in a nullable value type or a nullable reference type. Adding a constraint like where T : class removes the error, but it breaks existing code.

Actual Behavior:
Error CS0453.

@AlekseyTs

Pattern-matching spec out of date

The checked-in pattern matching spec is fairly out-of-date, and should be updated per recent discussion and implementation. Specifically,

  • The syntactic disambiguation that was added to support patterns in the is-expression needs to be described.
  • The use of a wildcard in a type pattern should be described.
  • There is an ambiguity between a positional pattern ( 1 ) and a constant pattern with a parenthesized expression. That needs to be resolved, and probably requires some design work.
  • The section on the scope of pattern variables should refer to the C# 7 specification.
  • The section on a user-defined operator is should be rewritten to refer to a Deconstruct method.
  • The spec doesn't describe subsumption as it applies to the switch statement (intuitively, that a case that cannot handle something already handled by a previous case is an error).
  • The match-expression requires some design work.
  • The throw-expression can be removed (it was added in C# 7)
  • I believe we decided to remove the "destructuring assignment" statement.
  • It isn't clear if we want a shorthand for a one-arm pattern-matching expression.

Also, some sections of the spec template are missing, such as links to design notes.

Syntax to choose CurrentCulture/InvariantCulture for interpolated strings

C# introduced interpolated string, but with no obvious way to decide which CultureInfo to use during formatting.

Currently it is possible to use InvariantCulture like this:

FormattableString.Invariant($"count: {count}")

This requires code to target .NET 4.6 and allocates the FormatabbleString instance instead of just generating call to string.Format.

This could be improved by adding some syntax to explicitly choose CultureInfo, like:

$"count: {count}"c    // this will call string.Format with CultureInfo.CurrentCulture
$"count: {count}"i    // this will call string.Format with CultureInfo.InvariantCulture
$"count: {count}"     // as it is now, this will call string.Format without CultureInfo, effectively CurrentCulture

This will compile to just string.Format and will not require targeting .NET 4.6.

Also it will be easy to add analyzer to enforce setting the c and i suffixes always, like we have now in FxCop for string.Format and others.

Champion "Records" (VS 16.8, .NET 5)

Champion "Pattern Matching" (16.3, Core 3)

  • Proposal added: proposals/patterns.md
  • Discussed in LDM
  • Decision in LDM
  • Finalized (done, rejected, inactive)
  • Spec'ed

Update: We take this to represent recursive patterns and match expressions. Active patterns should be tracked separately, as they won't happen at the same time.

LDM notes:

Proposal: move

Background

Linear transfer of ownership is a useful concept in programming. It’s the notion of taking some variable and handing out its value while at the same time making the original variable unusable for the value. For example, consider a type meant to serve as a data structure of work to be processed; it lets you add to it one element at a time and then extract from it the chunk of work previously added to it.

public sealed class WorkQueue<T>
{
    Queue<T> m_work;

    public void Enqueue(T item)
    {
        if (m_work == null) {
            m_work = new Queue<T>();
        }
        m_work.Enqueue(item);
    }

    public bool TryExtractAll(out Queue<T> queue)
    {
        queue = m_work;
        m_work = null;
        return queue != null;
    }
}

You wouldn’t want multiple calls to ‘TryExtractAll’ to return the same data, so the method explicitly nulls out the work queue after copying it and before returning it. Failure to null it out would silently result in the same elements being processed multiple times by the consumer.

As another example, consider implementing a basic stack data structure:

public sealed class SimpleStack<T>
{
    T[] m_items = new T[4];
    int m_count; 

    public int Count => m_count;

    public void Push(T item)
    {
        if (m_count == m_items.Length) {
            readonly T[] arr = new T[m_count * 2];
            Array.Copy(m_items, arr, m_count);
            m_items = arr;
        }
        m_items[m_count++] = item;
    }

    public T Pop()
    {
        readonly T item = m_items[--m_count];
        m_items[m_count] = default(T);
        return item;
    }
}

Here we null out the removed element before handing them back. From the perspective of processing the data, there’s no value in null’ing out the element in the underlying array, but there is from a correctness perspective: if the value isn’t zeroed out, the array will retain a reference to the data even if no one else is using it, and thus could artificially extend the lifetime of the data indefinitely (or until enough elements are added back to the collection to overwrite this slot).

Problem

Both TryExtractAll and Pop in these examples implement a linear transfer of ownership, copying some value and then nulling out the original. Given how common this is, and given the reliability bugs that can result from neglecting to null out the original, language support for the concept is beneficial (and as we’ll see later in this chapter, it’s also actually required for other scenarios).

Solution

Introduce a 'move' keyword. 'move' would provide the exact behavior being discussed here: extract some value, zero out the original, and hand back the copied value, similar to the following method:

static T Move<T>(ref T location)
{
    T value = location;
    location = default(T);
    return value;
}

and could be used as follows:

string s1 = "hello"; 
string s2 = move s1;
Debug.Assert(s1 == null);
Debug.Assert(s2 == "hello");

With 'move', we can now re-implement our previous examples. The code bodies shrink to the point where we can easily just use expression-body syntax to implement the members:

public bool TryExtractAll(out Queue<T> queue) => (queue = move m_work) != null;
...
public T Pop() => move m_items[--m_count];

In these examples, 'move' has not only helped to ensure proper behavior, it’s also reduced the amount of code we had to write to achieve the same functionality, and less code mean less chance for error.

It’s important to note that 'move' does not provide any atomicity guarantees. In other words, it doesn’t atomically extract the value and zero out the original. This means that you still need to be careful when performing an operation like this involving multiple threads concurrently accessing the same data, using methods like Interlocked.Exchange.

On its own, 'move' isn't particularly valuable; after all, its functionality can be achieved using a Move method like that previously shown. Its value comes from the compiler understanding the implications of it, which enables additional features to be implemented that rely on the compiler having this knowledge: see dotnet/roslyn#161.

Ability to create generic types with default containing type?

Take a look at the following piece of code:

class HomeController
{
    public Test(ILogger<HomeController> logger, IStringLocalizer<HomeController> localizer)
    {
    }
}

This usage of injecting generic interface with containing type is so common specially in asp.net core.

Is there any way to simplify this injection, like ILogger, IStringLocalizer or other forms.

This maybe oversimplification (since refactoring will correct usages of class names anyway), but it's overwhelming to type long class names over and over again.

This maybe a great addition for C# language. Please let me know what you think!

Thanks

Proposal: File scoped namespaces

Background

.NET style guidelines recommend having at most one namespace per file. The current namespace syntax only allows block scope, resulting in every file typically having all of its contents (save using statements) wrapped in a namespace block. This leads to both vertical space being wasted (the curly braces used to delimit the block) and more importantly, horizontal space being wasted, with all of the important content indented by an extra tab (if style guidelines are being followed). The indentation from the namespace block conveys no useful information- it is just code bloat.

using System.Collections.Generic;

namespace Foo 
{
    public class Bar
    {
          ... // Potentially hundreds of lines of code, 
              // all of which has an extra indentation because 
              // of the namespace but is not actually conveying any useful information
    }
}

Proposal

Allow a single namespace declaration per file that has no braces and therefore no block scope. Every class written in the file is a member of this namespace. It would be an error to attempt to introduce a second namespace declaration of any kind (including one with block scope). The single namespace declaration would be valid in the outermost scope only. It would be valid to appear at any part of the file in this scope, although style guidelines would recommend putting it at the top.

namespace Foo;

using System.Collections.Generic;

public class Bar
{
    ...
}

Previous Discussion

Code Plex

Uservoice (vote for the feature)

http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/7181921-file-scoped-namespaces

tuples and disposable objects

Hi,
I'm following the discussion about tuples, and I have a question about this.
Often I have methods the return disposable objects (the most common is a ef or ado.net db connection, and to be sure that are correctly dispose I use using (...

using( var db = obj.getDbConnection()) {
 ...
}

Now, if one of the members of the tuple is an object like this, how can I handle its disposal in a proper way? For example I can have a method that returns both an ado.net sqlconnection and the related transaction object, because I want to encapsulate the creation procedure.

Proposal: Exponentiation operator

Consider adding an exponentiation operator that is higher in precedence than the multiplicative operators but lower in precedence than the unary operators.

In the Symbolism computer algebra library, +, *, /, and - are overloaded to create Sum, Product, Quotient, and Difference objects respectively. ^ is also overloaded to create Power objects. However ^, being the logical XOR operator, is lower in precedence than the multiplicative operators. Thus parenthesis must be used in expressions like a + b * (c ^ e).

MSDN - C# Operators

Exponentiation operator in some other languages:

Language Operator
F# **
VB.NET ^
TypeScript **
JavaScript **
Python **
Haskell ^, ^^, **
OCaml **
Swift Not built-in but can be defined
FORTRAN **
Ada **
Nim ^
ALGOL 60 **
APL *

Language support for KeyValuePair<TKey, TValue> struct and tuple mappings

KeyValuePair is very often used in C# code in many scenarios.
As there is (k, v) tuple syntax support, I suggest same support for KeyValuePair widely used struct.
dotnet/roslyn#11530

As KeyValuePair and Tuple is a common pattern and sometimes there is custom Tuples or Keyed structs implemented, I suggest generalized built-in support for such tuple patterns and generalized support for keyed classes/structs at language level.

For such purposes I suggest new overloadable operators.

For tuple mappings (enable tuple-syntax initializing and tuple assignment)

public static (,) operator Class1(int field1, double field2)
{
    return new Class1() { Field1 = field1, Field2 = field2 }; 
}

and if class is generic:

public static (,) operator Class2<T1, T2>(T1 field1, T2 field2)
{
    return new Class2() { Field1 = field1, Field2 = field2 }; 
}

This will allow such things:

public Class1 SomeFunc()
{
    // assignment
    Class1 c = (2, 5.0);

    // return value
    return (1, 1.0);
}

So, KeyValuePair<TKey, TValue> should also support tuple operator and instead of new KeyValuePair<string, string>("key", "value") you can just write ("key", "value") if you're returning KeyValuePair from function, assign it to variable or use it as parameter for method.

And for object keys support.

// usage: extract key from object if it support keying.
var key = obj.!;

// define "key" operator
public static int operator .!(Class1 obj)
{
    return obj.Key;
}

// "keyed" constraint in generics

public interface IInterface1<T> where T: with key
{
}

Support for "value" operator

// usage: extract "value" from object
var value = obj.*;

// define "value" operator

public static string operator .*(Class1 obj)
{
    return obj.Value;
}

// if .* operator is not overloaded .* operator should return object itself.
// so default implementation for .* operator should be this, just returning object itself.

// default implementation of .* operator
public static Class2 operator .*(Class2 obj)
{
    return obj;
}

Extract types of keys and values at compile-time

Type keyType = typeof(Class1.!);  // get type of key
Type valueType = typeof(Class1.*);  // get type of value

Use types of keys and values in generics

public interface IKeyedCollection<T, T.!> : IDictionary<T.!, T> where T : with key
{
    void Add(T value);
    void Remove(T.! key);
}

public interface IKeyedDictionary<T, T.!, T.*> : IDictionary<T.!, T.*> where T : with key
{
    void Add(T value);
    void Remove(T.! key);
    T.* GetValue(T.! key);
}

T.! meaning type of key and T.* meaning type of value

And implicit (or explicit) operator conversion from keyed objects for existing APIs compatibility

// built-in implicit conversion for KeyValuePair from any keyed object
public static implicit<T> operator KeyValuePair<T.!, T.*>(T obj)
    where T: with key
{
    return new KeyValuePair<T.!, T*>(obj.!, obj.*);
}

Also added <T> for implicit<T> operator, so implicit conversions can accept generic source object argument

namespace internal access modifier

edited
internal is assembly wide but sometimes I want the scope to be narrower; something like namespace internal so it isn't visible to the entire project, but just items in that namespace.

When the assembly is created this could translate just to internal

namespace Something
{
    public class Thing
    {
        private ThingsThing _thing;

        public ThingsThing MyThing => _thing;
    }

    public struct ThingsThing
    {
        public object Stuff { get; namespace internal set; }

        public void DoThings();

        namespace internal void DoThatThing();
    }
}

--- old ---
Thought this was part of private protected but doesn't seem to be

I'd like a way of mark something of a nested type public to the enclosing type but private for everything else (or protected if visible to derived types)

public class Thing
{
    private ThingsThing _thing;

    public ThingsThing MyThing => _thing;

    public struct ThingsThing
    {
        public object Stuff { get; enclosing set; }

        public void DoThings();

        enclosing void DoThatThing();
    }
}

Proposal: Immutable Types

Problem

One of the uses of 'readonly' fields is in defining immutable types, types that once constructed cannot be visibly changed in any way. Such types often require significant diligence to implement, both from the developer and from code reviewers, because beyond 'readonly' there’s little assistance provided by the compiler in ensuring that a type is actually immutable. Additionally, there’s no way in the language (other than in type naming) for a developer to convey that a type was meant to be immutable, which can significantly impact how it’s consumed, e.g. whether a developer can freely share an instance of the type between multiple threads without concern for race conditions.

Consider this type:

public class Person
{
    public Person(string firstName, string lastName, DateTimeOffset birthDay)
    {
        FirstName = firstName;
        LastName = lastName;
        BirthDay = birthDay;
    }

    public string FirstName { get; }
    public string LastName { get; }
    public DateTime BirthDay { get; }

    public string FullName => $"{FirstName} {LastName}";
    public TimeSpan Age => DateTime.UtcNow – BirthDay;
}

Writing this type requires relatively minimal boilerplate. It also happens to be immutable: there are no exposed fields, there are no setters on any properties (the get-only auto-props will be backed by 'readonly' fields), all of the fields are of immutable types, etc. However, there is no way for the developer to actually express the intent to the compiler that an immutable type was desired here and thus get compiler checking to enforce this. At some point in the future, a developer could add a setter not realizing this type was meant to be immutable, and all of a sudden consumers of this type that were expecting full immutability (e.g. they'd avoiding making defensive copies) will now be very surprised:

public class Person
{
    public Person(string firstName, string lastName, DateTimeOffset birthDay)
    {
        FirstName = firstName;
        LastName = lastName;
        BirthDay = birthDay;
    }

    public string FirstName { get; }
    public string LastName { get; }
    public DateTime BirthDay { get; set; } // Oops!

    public string FullName => $"{FirstName} {LastName}";
    public TimeSpan Age => DateTime.UtcNow – BirthDay;
}

Similarly, the class could be augmented with an additional 'readonly' property but of a non-immutable type:

public class Person
{
    public Person(string firstName, string lastName, DateTimeOffset birthDay, Person[] ancestors)
    {
        FirstName = firstName;
        LastName = lastName;
        BirthDay = birthDay;
        Ancestors = ancestors;
    }

    public string FirstName { get; }
    public string LastName { get; }
    public DateTime BirthDay { get; }
    public Person[] Ancestors { get; }; // Oops!

    public string FullName => $"{FirstName} {LastName}";
    public TimeSpan Age => DateTime.UtcNow – BirthDay;
}

And so on. The developer has tried to design an immutable type, but without a way to declare that fact, and without compiler verification of that declaration, it is easy for bugs to slip in.

Solution: Immutable Types

We can introduce the notion of immutable types to C#. A type, either a class or a struct, can be annotated as "immutable":

public immutable class Person
{
    public Person(string firstName, string lastName, DateTimeOffset birthDay)
    {
        FirstName = firstName;
        LastName = lastName;
        BirthDay = birthDay; 
    }

    public string FirstName { get; }
    public string LastName { get; }
    public DateTime BirthDay { get; }

    public string FullName => $"{FirstName} {LastName}";
    public TimeSpan Age => DateTime.UtcNow – BirthDay;
}

When such an annotation is applied, the compiler validates that the type is indeed immutable. All fields are made implicitly readonly (though it’s ok for a developer to explicitly state the ‘readonly’ keyword if desired) and be of immutable types (all of the core types like Int32, Double, TimeSpan, String, and so on in the .NET Framework would be annotated as immutable). Additionally, the constructor of the type would be restricted in what it can do with the 'this' reference, limited only to directly reading and writing fields on the instance, e.g. it can’t call methods on 'this' (which could read the state of the immutable object before it was fully constructed and thus later perceive the immutable type as having changed), and it can’t pass 'this' out to other code (which could similar perceive the object changing). This includes being prohibited from capturing 'this' into an anonymous method in the ctor. A type being 'immutable' doesn't mean that its operations are pure, just that the state within the object can't observably change; an immutable type would still be able to access statics, could still mutate mutable objects passed into its methods, etc.

The 'immutable' keyword would also work as an annotation on generic types.

public immutable struct Tuple<T1, T2>
{
    public Tuple(T1 item1, T2 item2) { Item1 = item1; Item2 = item2; }
    public T1 Item1; // Implicitly readonly
    public T2 Item2; // Implicitly readonly
}

Applying 'immutable' to a type with generic parameters would enforce all of the aforementioned rules, except that the generic type parameters wouldn't be enforced to be immutable: after all, without constraints on the generic type parameters, there’d be no way for the implementation of the open generic to validate that the type parameters are immutable. As such, a generic type annotated as 'immutable' can be used to create both mutable and immutable instances: a generic instantiation is only considered to be immutable if it’s constructed with known immutable types:

void Usage<U>()
{
    Tuple<string, string>         local1; // considered immutable
    Tuple<int, string>            local2; // considered immutable
    Tuple<int, IC>                local3; // considered immutable
    Tupe<Tuple<int, string>, int> local4; // considered immutable
    Tuple<string, U>              local5; // considered mutable
    Tuple<C, C>                   local6; // considered mutable
    Tuple<Tuple<int, C>, int>     local6; // considered mutable
}
immutable class IC { }
class C { }

Such concrete instantiations could be used as fields of other immutable types iff they're immutable. But whether a generic instantiation is considered to be immutable or not has other effects on consumers of the type, for example being able to know that an instance is immutable and thus can be shared between threads freely without concern for race conditions. As such, the IDE should do the leg work for the developer and highlight whether a given generic instantiation is considered to be immutable or mutable (or unknown, in the case of open generics).

However, the immutability question also affects other places where the compiler needs to confirm that a type is in fact immutable. One such place would be with a new immutable generic constraint added to the language (there are conceivably additional places in the future that the language could depend on the immutability of a type). Consider this variation on the tuple type previously shown:

public immutable struct ImmutableTuple<T1, T2>(T1 item1, T2 item2) 
    where T1 : immutable
    where T2 : immutable
{
    public ImmutableTuple(T1 item1, T2 item2) { Item1 = item1; Item2 = item2; }
    public T1 Item1;
    public T2 Item2;
}

The only difference from the previous version (other than a name change for clarity) is that we’ve constrained both generic type parameters to be 'immutable'. With that, the compiler would enforce that all types used in generic instantiations of this type are 'immutable' and satisfy all of the aforementioned constraints.

void Usage<U>()
{
    ImmutableTuple<string, string>         local1; // Ok
    ImmutableTuple<int, string>            local2; // Ok
    ImmutableTuple<int, IC>                local3; // Ok
    ImmutableTupe<Tuple<int, string>, int> local4; // Ok
    ImmutableTuple<string, U>              local5; // Error: ‘U’ is not immutable
    ImmutableTuple<C, C>                   local6; // Error: ‘C’ is not immutable
    ImmutableTuple<Tuple<int, C>, int>     local6; // Error: ‘Tuple<int,C>’ is not immutable
}
immutable class IC { }
class C { }

With such constraints, it’s possible to create deeply immutable types, both non-generic and generic, and to have the compiler help fully validate the immutability.

However, there are times when you may want to cheat, where you want to be able to use the type to satisfy immutable constraints, and potentially have some of the type’s implementation checked for the rules of immutability, but where you need to break the rules in the implementation in a way that’s still observably immutable but not physically so. For example, consider building an ImmutableArray type that wraps an underlying array. As arrays are themselves mutable (code can freely write to an array’s elements), it’s not normally possible to store an array as a field of an immutable type:

public immutable class ImmutableArray<T>
{
    readonly T[] m_array; // Error: The types of fields in immutable types must be immutable}

To work around this, we can resort to unsafe code. Marking an immutable type as 'unsafe' would disable the rule checking for immutability in the entire type and put the onus back on the developer to ensure that the type really is observably immutable, while still allowing the type to be used in places that require immutable types, namely generic immutable constraints. Marking a field as unsafe would disable the rule checking only related to that field, and marking a method as unsafe would disable the rule checking only related to that method. A type that uses unsafe needs to ensure not only that it still puts forth an immutable facade, but that its internal implementation is safe to be used concurrently.

public immutable unsafe struct ImmutableArray<T>
{
    readonly T[] m_array; // Ok, but we’re now responsible again for ensuring immutability

    private ImmutableArray<T>(T[] array) { m_array = array; }

    private ImmutableArray<T>(T[] array, T nextItem) : this(new T[array.Length + 1])
    {
        Array.Copy(array, m_array, array.Length);
        m_array[array.Length] = nextItem;
    }

    public ImmutableArray<T> Add(T item) => new ImmutableArray<T>(m_array, item);

    public T this[int index] => m_array[index];
    public int Length => m_array.Length;
    ...
}

Delegates could also be marked as immutable, and a set of ImmutableAction and ImmutableFunc types would be included in the framework. As with other immutable types, all of the objects reachable from an immutable delegate instance would need to be immutable, which means that an immutable delegate could only bind to methods on immutable types. That in turn means that, when an anonymous method binds to an immutable delegate type, that anonymous method may only capture immutable state. Further, any locals captured into the lambda must either be from a 'readonly' value (#115) or must be captured by value (#117). This ensures that the fields of the display class can be 'readonly' and that the method which created the lambda can’t reassign the captured values after creating the lambda.

public void Run()
{
    readonly int local1 =;
    int local2 =;
    C local3 =;

    ImmutableAction action1 = () => {
        Console.WriteLine(local1.ToString()); // Ok, captured readonly immutable
        Console.WriteLine(local2.ToString()); // Error: ‘local2’ must be captured by value
        Console.WriteLine(local3.ToString()); // Error: ‘local3’ is mutable
    };

    ImmutableAction action2 = [val local2]() => {
        Console.WriteLine(local2.ToString()); // Ok, captured non-readonly immutable by value
        local2 = 0;                           // Error: ‘local2’ is readonly
    };
}

Alternatives

The 'immutable' attribution would be deep, meaning that an instance of an immutable type and all of the types it recursively references in its state would be immutable. In contrast, we could consider a shallow version, with a 'readonly' attribute that could be applied to types. As with 'immutable', this would enforce that all fields were readonly. Unlike 'immutable', it would place no constraints on the types of those fields also being immutable.

Question: why System.Void isn't accessible from C#?

This code

var l = new List<System.Void>();

don't compile with error

System.Void cannot be used from C# -- use typeof(void) to get the void type object

Well, List<System.Void> is meaningless. But there are scenarios when value type meaning literally "nothing" is needed.

Imagine this:

public interface IActionWithResult<T>
{
    Task<T> Execute();
}

and class, implementing this interface

// we don't return any result as there is no result
public class MyActionWithoutResult : IActionWithResult<System.Void>
{
    public Task<System.Void> Execute()
    {
        // some code
    }
}

wouldn't compile with the same error

System.Void cannot be used from C# -- use typeof(void) to get the void type object

Because of it there are different delegate types and many other - Action<T> and Func<T1, T2>
If C# have similar to F# Unit built-in type, there will be no need for separate Action and Func types.

On CLR level void type seems to be of value type System.Void, but this type (value meaning literally "nothing") isn't accessible from C#. Why?

Is it CLR limitation?

Proposal: extend the set of things allowed in expression trees

Expression trees support only a subset of the things that could be semantically meaningful. Consider allowing them to support the following constructs:

  • The null-coalescing operators such as a?.b, a?[b]
  • Expressions involving dynamic
  • Invocations that use default or named parameters
  • Assignment and other mutation expressions
  • A statement-bodied lambda

Constraint missing (async function must return one of these types) in draft C# specification

The following program

using System;
class C
{
    static void Main()
    {
        // error CS4010: Cannot convert async anonymous method to delegate type 'System.Func<int>'. An async anonymous method may return void, Task or Task, none of which are convertible to 'System.Func<int>'.
        Func<int> x = async delegate {
            throw null;
        };
    }
}

Is rejected by the compiler.

Seeking guidance from the language spec, I could not find anything to forbid this program. Perhaps there is a constraint missing in 6.5 (“If F is async, D has the return type void, Task, or Task<T> for some T.”) or misworded?

Add pre-2015 meeting notes

LDM meeting notes from 2015 onward are being added to this repository in #7. However, meeting notes from October 2013 through the end of 2014 are (linked) in

and

Although their source is in markdown form, only the author can easily see the markdown source. So can I please have some help, either from an administrator, or from @MadsTorgersen and Lucian, on getting the markdown source for those?

Fixed/moveable variables versus locals captured in a lambda

The language spec (18.3 Fixed and moveable variables) says

In precise terms, a fixed variable is one of the following:

  • A variable resulting from a simple-name (§7.6.2) that refers to a local variable or a value parameter, unless the variable is captured by an anonymous function.
  • A variable resulting from a member-access (§7.6.4) of the form V.I, where V is a fixed variable of a struct-type.
  • A variable resulting from a pointer-indirection-expression (§18.5.1) of the form *P, a pointer-member-access (§18.5.2) of the form P->I, or a pointer-element-access (§18.5.3) of the form P[E].
    All other variables are classified as moveable variables.

But testing this on the following program

using System;

unsafe struct S
{
    public fixed int buffer[1];
    public int i;
}

unsafe class Test
{
    private void example1()
    {
        S data = new S();
        Func<S> lambda = () => data;
        fixed (int* p = &data.i) // ok; data is captured
        {
        }
        fixed (int* p = data.buffer) // ok; data is captured
        {
        }
    }
}

We find that Roslyn reject both attempts, complaining that you can't take the address of a variable that has been captured by a lambda, AND that you can't use the fixed statement to take the address of an already fixed expression.

Why does the language forbid using a fixed statement for a fixed variable? It would just be a no-op. Allowing it would sidestep having to specify which variables are captured in an async method. We could just say that there are no fixed variables in an async method, and force people to use the fixed statement (which might sometimes be a no-op).

Proposal: Extension Methods on Method Groups

Can you please allow extension methods to be called on method groups or lambdas via method group conversion to a delegate or expression type?

You should then also allow deconstructing assignments of method groups & especially lambdas (via public static void Deconstruct(this Expression<Func<...>>, out ...)).

Benefits

This would allow a number of useful extension methods, such as

var inTemp = Path.Join.Curry(Path.GetTempPath());
var toUpperCase = "".ToUpper.RemoveTarget();  // or ToOpenDelegate() or UnCurry()
var isValid = Path.IsPathRooted.And(new RegEx(@"...").IsMatch);

stuff.Select((o => ...).WithErrorHandling());

(object target, MethodInfo method) = "".ToUpper;
(ParameterExpression param, Expression body) = (string p) => p.Whatever();

Risks

Very little; none of this is currently valid syntax, so this should not be a breaking change.
This would remove the following errors:

  • CS0023: Operator '.' cannot be applied to operand of type 'lambda expression'
  • CS0119: 'C.M(Action)' is a method, which is not valid in the given context
  • CS8131: Deconstruct (sic) assignment requires an expression with a type on the right-hand-side

This could be a bit confusing if a method group and extension method have multiple matching overloads, but that issue already exists when calling non-extension methods.

Implementation

All of these examples are already callable without extension syntax, using delegate conversions on the first parameter. Just allow these conversions when selecting extension methods too.

Proposal: Attributes everywhere

Background

Today, attributes have a few restrictions relevant to this conversation:

  • They can only be used in places allowed by their AttributeUsage, which is itself limited to where attributes can be represented in IL. This means they can’t be used in the body of methods.
  • They must derive from Attribute.
  • They are always output to the MSIL, unless annotated as Conditional with an appropriate compilation constant, but that Conditional applies to the type definition, and thus any instance of that attribute abides by that same conditionality.

Proposal

  • Whereas today we have the […] syntax for attributes, I propose we introduce an additional [[…]] syntax.
  • […] remains exactly as it is today, whereas [[…]] represents attribution that is source-only and will never be emitted to IL. Thus you can write:
[SomeAttribute(1, 2)]
[[SomeAttribute(3,4)]]
class C { }

and the first attribute will end up in metadata whereas the latter won’t.

  • The […] continues to be limited by AttributeUsage, whereas [[…]] doesn’t. [[…]] can be used anywhere in source where we can make it syntactically unambiguous (which should be most, if not all, locations). The purpose of this is to have it represented as a special kind of syntax node in a syntax tree available to analyzers, likely as another form of trivia.
  • [[…]] would also not be limited to types derived from Attribute. Arbitrary expressions could be used in the , with those expressions showing up as children of the special syntax node in the syntax tree. For example, you could do things like:
public class NoCaptureAttribute : Attribute { }
public static class Allocs { public static void AllowCapture(params object[] args) { } }[[NoCapture]] i => i * this.Value; // error from analyzer… lambda captures when it’s not allowed to
[[Allocs.AllowCapture(this)]] i => i * this.Value; // analyzer ok… lambda allowed to capture this

or:

throw new Exception( [[Issue(123)]]“message to be localized” ); // analyzer dispays info about issue from github

or:

Console.WriteLine($"The value is {[[BoxOk]]this._result}."); // analyzer warns about boxing unless it’s annotated
  • Since the attribute would never be serialized to metadata, libraries could depend on “attributes” but not need to ship those attributes, making some customers more willing or able to take a dependency on an analyzer library in their source. And for cases where that’s not even possible, this could be done without special types at all, e.g.
Console.WriteLine($"The value is {[["boxok"]]this._result}.");

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.