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] Add ability to declare global usings for namespaces, types and aliases by using a command line switch #3428

Open
AlekseyTs opened this issue May 6, 2020 · 159 comments
Assignees
Labels
Feature Request Implemented Needs ECMA Spec This feature has been implemented in C#, but still needs to be merged into the ECMA specification Proposal champion Proposal
Milestone

Comments

@AlekseyTs
Copy link
Contributor

AlekseyTs commented May 6, 2020

Motivation - provide shared context for the program to reduce repetition of common using directives across all source files. For example, using System;, etc. VB compiler supports that for many years.

Specification: https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/GlobalUsingDirective.md

@theunrepentantgeek
Copy link

theunrepentantgeek commented May 6, 2020

I don't see the value-add of this - it lessens the ability of readers to reason about the C# source code their reading by adding mystery.

@jnm2
Copy link
Contributor

jnm2 commented May 6, 2020

Far from being mysterious, these chunks at the tops of each file are often large and obvious, and they are rarely interesting to look at. They are a frequent source of noise in diffs where they are usually just as uninteresting as when reading a source file.

There is no option for you if any of this bothers you today. The IDE could have an autocollapse feature for using directives, but that wouldn't do anything to help other experiences. The number of times this has come to mind has overcome the skepticism I had on this a few years ago. I'm happy to see that an option is being provided.

@YairHalberstadt
Copy link
Contributor

@louthy might find this interesting for his language-ext package. Currently you have to add

using LanguageExt;
using static LanguageExt.Prelude;

To every file. If you could specify in the csproj that every file should have those preimported it would make his language extensions feel much more idiomatic.

@gerhard17
Copy link

I often came to the point wanting a project wide type alias: using xxx = sometype;
This desire was mainly driven by library development purposes, not by usage aspects.

A project wide using static could be smart also. (but I had never a real need for this)

@YairHalberstadt
Copy link
Contributor

I often came to the point wanting a project wide type alias:

I feel less comfortable about this. I feel like if C# ever introduces project wide type aliases, it should be a first class language feature, rather than a compiler switch.

@amis92
Copy link
Contributor

amis92 commented May 6, 2020

My opinion: I really like _Imports.razor from ASP.NET Core Razor framework; I'd like to see something similar for C# as well. :)

@alrz
Copy link
Contributor

alrz commented May 6, 2020

Instead of cli option, could be considered as global aliases. There is a handful of proposals in that space already. For instance:

// Imports.cs
global using System;
global using static Helpers;
global using Alias = TypeOrNamespace;

On top of that, #1239 is championed as well which could be used together.

global using ServiceResult<T> = Result<T, ServiceError>;

An important question would be metadata encoding outlined by @333fred in #259 (comment)

@Richiban
Copy link

Richiban commented May 6, 2020

I also support the concept of global usings in the project file. Although there'd be no way top opt out of these auto-usings per file, so it's possible there'd be clashes. There are ways around this though, so maybe it's not too bad.

@HaloFour
Copy link
Contributor

HaloFour commented May 6, 2020

As noted above, VB.NET already has this (and has since inception), and it has never been considered a source of confusion. Most users would interact with it via their project settings. I personally don't find it to be any different or more confusing than the fact that you can add references from the command line which has just as profound impact on the code you are reading.

@alrz

On top of that, #1239 is championed as well which could be used together.

If that proposal is also implemented it might complicate this proposal as it involves characters that can't be used on the command line. Replacement characters would be required. It wouldn't be a big deal, but something to keep in mind.

🍝

-using:StringDict(T)=System.Collections.Generics.Dictionary(System.String,T)

@alrz
Copy link
Contributor

alrz commented May 6, 2020

I expect generic type aliases to be able to define generic constraints. Also it could be a possiblity to enable "public" type aliases in the future. This proposal doesn't make the transition any easier.

I don't think there's any sane way to encode that in an string except for literally writing a possibly slightly diffrent C# dialect inside the command line, plus you'd lose any IDE goodies like autocomplete or goto declaration. That will need an expensive tooling support for a trasparent experience.

We're practically exposing language semantics outside of the language itself to an external source - this doesn't seem like a good idea IMO.

@HaloFour
Copy link
Contributor

HaloFour commented May 6, 2020

@alrz

I expect generic type aliases to be able to define generic constrains. Also it could be a possiblity to enable "public" type aliases in the future. This proposal doesn't make the transition any easier.

That's not a part of that championed proposal, although it's fair to bring it up as a possibility. I'd think that this feature would be orthogonal to public aliases. It's also much simpler to implement, and it doesn't necessarily have to support the full breadth that using declarations might support.

That will need an expensive tooling support for a trasparent experience.

VB.NET already (and has always) exposed this option through the tooling experience in Project Settings. IMO that's not a problem.

@alrz
Copy link
Contributor

alrz commented May 6, 2020

I'd think that this feature would be orthogonal to public aliases. It's also much simpler to implement, and it doesn't necessarily have to support the full breadth that using declarations might support.

I believe it's actually very similar to an "internal using directives" feature which itself is a subset of global imports. All the mentioned features could fit nicely in an "enhanced using" umbrella, but this proposal as a command line option does not contribute to that possible set of improvements and likely becomes unpreferable when we have those features in place.

VB.NET already (and has always) exposed this option through the tooling experience

For me, it's understandable if VB does that. I think VB is a lot more "forgiving" in different aspects than C# (more implicit conversions, non-constants in case clauses, to name a few). So it's accepted that we even have a few imports by default. Maybe it worked out well for the target audience, but C# has always been a little more strict.

My argument is that any code that has meaning e.g. affects binding, should be a part of the source. The second you're out of source, the experience tends to degrade, in some sense.

@jnm2
Copy link
Contributor

jnm2 commented May 6, 2020

Whether this is implemented via new C# syntax or CLI parameters, the SDK will be able to build on top of this and do <GlobalUsing Include="System" /> csproj items.

@HaloFour
Copy link
Contributor

HaloFour commented May 6, 2020

Hopefully the LDM discussion on this subject will include global aliases and maybe exported aliases.

IMO the experience isn't any different if the namespaces/aliases are declared in the CLI vs. in a source file that could potentially be very far removed from the source file that you're currently looking at. I stopped using VB.NET around the time that C# 3.0 was released but I do know that I made use of project-wide imports.

@CyrusNajmabadi
Copy link
Member

I don't agree with this statement:

As noted above, VB.NET already has this (and has since inception), and it has never been considered a source of confusion.

Even on roslyn itself it has been a point of pain and confusion numerous times :)

@HaloFour
Copy link
Contributor

HaloFour commented May 6, 2020

@CyrusNajmabadi

Even on roslyn itself it has been a point of pain and confusion numerous times :)

Sounds like a good point to bring up in LDM. But is the nature of the feature confusing, or is it confusing because it's an aspect of VB.NET and not C#? As a VB.NET user for some time I never found it confusing. I found it more confusing that C# didn't support it. I'd be interested to hear a poll of VB.NET users.

@ericwj
Copy link

ericwj commented May 6, 2020

Anything that would be global would defeat a large number of use cases.

At the very least any sort of feature like this would need to be opt-in, either on a per-code-file or per-folder basis like the razor features that are mentioned.

At least this holds for how I use #defines if I use many of them and I see a lot of requests where this is used to change basic types, but while a compiler switch or other global configuration may work for applications I reckon it will create major headaches trying to extend this into libraries.

@gafter gafter added this to TRIAGE NEEDED in Language Version Planning May 7, 2020
@333fred 333fred moved this from TRIAGE NEEDED to 10.0 Candidate in Language Version Planning Jul 13, 2020
@333fred 333fred added this to the 10.0 candidate milestone Jul 13, 2020
@munael
Copy link

munael commented Jul 24, 2020

// MyCommon.cs
namespace MyCommon {
	public using System;
	public using MyTypeA = Quux.MyTybe<A>;
	public using ...;
}

// Foo/Bar.cs
using MyCommon;
// MyTybeA ...
// ...

The above:

  1. Enables all use cases outlined in this proposal (you can put the public usings under an the top namespace)
  2. Enables use cases that inevitably be requested ("global using per folder", for example)
  3. Enables libraries to provide their own "using bundles"
  4. Is accessible and editable with all the tools of an IDE
  5. Seamlessly gels with any future features of using declarations

Personally I don't like the idea of "invisible" usings. They're invisible because usually one doesn't need to play with the build system often. And the core idea of reducing the number of lines of usings has so much more potential.

@HaloFour
Copy link
Contributor

@narfanar

I'm not sure that approach is any better. Those "using"s are just as invisible, now they're just packaged in other namespaces. Now importing a single namespace could also introduce any number of other namespaces or aliases which could introduce ambiguities or cause code to be silently reinterpreted. At least a project-level setting is explicitly determined by the developer.

@CyrusNajmabadi
Copy link
Member

CyrusNajmabadi commented Sep 28, 2020

Something i forgot to mention in teh LDM meeting:

IDE has already put in a bunch of features to make 'usings' less relevant. For example, a common reason specified for having these usings automatically added is so that you can immediately just start using the types/extensions from it without needing to manually add the using first. However, IDE has a feature (which we've just switched to being 'on by default') whereby we will include all members from all namespaces in teh completion list, and will add the using for you automatically if you commit it.

This makes it easy to browse and find things (i.e. you can just type Xml or File or Stream) and then add what you need for that file.

As such, an empty file is a totally reasonable place to start using C#. With top level statements you can have that, and just type what you want (adding imports as necessary for the things you use).

@333fred 333fred removed this from the 10.0 candidate milestone Sep 28, 2020
@lukemcdo
Copy link

Game developers using MonoGame regularly do this, it's considered best practice within that community. I am not a MonoGame expert and cannot quickly find examples. Specifically, the Streets of Rage developer described this as a monogame best practice.

In summation: you're not aware of any clear and specific examples where this feature could cause a real problem, but you find the feature objectionable because one developer in a niche community recommends an archaic methodology that some projects in that community might use and which might be very briefly broken if these projects decide to opt in to the new language feature and configure it in a way that happens be to be incompatible.

Not the most robust of defenses.

The alternative is effectively to brigade, and I'm not interested in doing that, it's not fair to anyone here. If you've seen the CoreRT -> NativeAOT threads you would prefer this approach.

@CyrusNajmabadi
Copy link
Member

The alternative is effectively to brigade,

I guess i just don't see what you're lookign for here. Your position seems speculative and disagrees with our lived experiences with the same feature. Similarly, you seem to feel that any potential workarounds (for a group you admit is niche, and which you haven't actually demonstrated an issue for) are not acceptable. None of these seems like it could be sufficient to stop this optional feature from being made available to users who want it.

@lukemcdo
Copy link

I take it doing this would also supersede globals if the user added to the same namespace in their own file?

Yes.

This is how usings have always worked. We lookup names inside-out (which is generally how people intuit things).

As a global-using is equivalent to placing the using at the top of the file, any nested usings take precedence.

I can't even figure out if that's a good thing or not.

It's how the language has always worked. Nested usings take precedence.

I meant in a secondary file -- it wasn't clear to me whether the "using" statements were visible across files because they're in a namespace. They don't appear to be from my quick testing. This was a matter of understanding your proposed workaround. It seems okay.

@CyrusNajmabadi
Copy link
Member

Gotcha. Thanks.

@lukemcdo
Copy link

The alternative is effectively to brigade,

I guess i just don't see what you're lookign for here. Your position seems speculative and disagrees with our lived experiences with the same feature. Similarly, you seem to feel that any potential workarounds (for a group you admit is niche, and which you haven't actually demonstrated an issue for) are not acceptable. None of these seems like it could be sufficient to stop this optional feature from being made available to users who want it.

The biggest problems I'm seeing are mostly through how it will be used. I am frustrated that you're actively refusing to consider how it can be used negatively to cause problems in a language that, above, you acknowledge is essentially complete.

@CyrusNajmabadi
Copy link
Member

you acknowledge is essentially complete.

I don't know where this position came from. Our language is not 'essentially complete' in any way shape or form.

@CyrusNajmabadi
Copy link
Member

I am frustrated that you're actively refusing to consider how it can be used negatively to cause problems

I don't see how I, or anyone else, has refused to consider anything. All your points have been addressed. Workarounds have been given. Advice has been provided on this topic. Appropriate 'next steps' have been given for projects that would like to use this feature but which may run into the issues you've mentioned.

I cannot see at all how you can represent these posts as 'refusing to consider' anything. THere may be a disagreement with you on the severity of your concerns and if they warrant any further steps on our part. However, that's not at all akin to refusing to consider what you are saying.

@lukemcdo
Copy link

but it's essential when I need it. Global usings are much the same.

I don't see how it can be essential given we've have 20+ years of not having it, and people have been able to be totally successful without them all this time. I think we're well past the point that anything is actually essential in the language.

This is what looked as if to say C# is "essentially complete."

You are considering what I am saying, but you're unwilling to acknowledge the responsibility of steering the direction of C# programming. Your response has been "it is optional" as a deflection. I've provided an example of where it inflicts difficulty and forces maintenance changes in a way many new language features do not. It either costs end users, or costs another team the effort of averting the ship. All for something that is "not essential."

@focustense
Copy link

The alternative is effectively to brigade, and I'm not interested in doing that, it's not fair to anyone here.

The alternative is to have material knowledge about the people and projects being cited as examples. What's "not fair" is flooding the discussion with hypothetical, vaguely-worded objections, then deflecting criticism as an attack on some user community that hasn't voiced any concern and didn't ask you to represent them.

As of this moment, there still has not been a single clear, specific, realistic example of where this could cause a problem.

I've provided an example of where it inflicts difficulty and forces maintenance changes

I see a lot of squid ink but no concrete examples.

@CyrusNajmabadi
Copy link
Member

This is what looked as if to say C# is "essentially complete."

It isn't. It's saying it's not an essential feature. That's a different statement from saying our language is essentially complete.

@CyrusNajmabadi
Copy link
Member

but you're unwilling to acknowledge the responsibility of steering the direction of C# programming

Literally every decision we make steers the direction fo the C# language. That is the objective definition of our job. Every time we ratify a feature it is precisely because we think it's a good decision for the future direction of hte language.

Your response has been "it is optional" as a deflection.

No. It's a reasonable response for a feature. You don't get to handwave it aware because you don't like it. This is optional. Users can choose if this is valuable for their codebases or not. We've got decades seeing people do this. You've brought up some cases where users might run into issues that honestly seem extremely insignificant. Based on this, i don't see any change in opinion on this feature.

I've provided an example of where it inflicts difficulty

Yes. And your situation was considered, and very trivial workarounds were provided if that project chooses to use this feature. As mentioned above, the project does not need to use the feature. And, if it decides the value is there, then resolving the possible conflicts is trivial. As such, since the impact is so minor, and the solution is so simple, this is not somethign that changes the path here.

It either costs end users,

It only costs if you decide to use the feature and that happens to cause a conflict. As stated numerous times already, we have experience with this and it has not been an issue for customers for decades.

All for something that is "not essential."

Yes. That is a call we have decided is utterly reasonable. Nearly all users will be able to use thsi without issue. All users will be able to not use this feature if they don't want. And, as demonstrated here, if a user wants to use this feature and runs into this issue, the workarounds are simple and easy to apply.

@CyrusNajmabadi
Copy link
Member

@lukemcdo I have not seen you address the core issue that we have experience with this and with multiple decades of this being something supported in a language we've shipped with millions of users. We literally have seen this be fine for an enormous ecosystem. So your concerns that there is some hidden group out there that has not spoken up, but which will be dramatically harmed by an optional feature they dont' need to use rings enormously hollow.

@CleanCodeX
Copy link

using "global using System.Linq" does not correctly recognize linq extension methods. Using is still necessary for extensions. Any solution for this planned?

This sounds like a straight up bug. Please file issue.

I was preparing the Issue and tried to find the cause for that bug. It is kind of a lack of support, but not on the dotnet team's side. It's on the Resharper's side. Once again fooled by Resharper not supporting new stuff, even when using the "early" EAP version. Global usings are obviously not yet supported by Resharper for extensions. So no problem here. Turning off resharper prevents the wrong "Cannot resolve symbol '{name}'" popup. It compiled fine anyways. (I didn't see that)

@CleanCodeX
Copy link

"1. "global using System;" leads to many warnings in automatic generated code files because they aren't aware of that global using, therefore using System will still be added when creating new files. Any solution possible rather than not adding System as a global using?"

Anytime my AssemblyInfo.cs is rewritten automatically it does not respect the global usings file. Can someone doublecheck this issue?

@333fred
Copy link
Member

333fred commented Aug 3, 2021

@CleanCodeX see https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-06-21.md#email-decision-duplicate-global-using-warnings. That change just hasn't been reflected yet.

@LeonG-ZA
Copy link

LeonG-ZA commented Aug 4, 2021

I haven't followed this thread in a while so I'm not sure what is and what isn't allowed in global usings.
Can I suggest that 'static usings' not be allowed in the global usings file?

@bernd5
Copy link
Contributor

bernd5 commented Aug 4, 2021

@LeonG-ZA that is to late. Static using is globally allowed, too.

The following works today:

global using static System.Console;

WriteLine("Foo");

@LeonG-ZA
Copy link

LeonG-ZA commented Aug 4, 2021

Well that sucks, I guess it it is up to the individual teams to discuss what is allowed and what isn't. It takes time to figure out what certain methods do, because of the naming.

//An example of how irritating this feature is in single files:

public void Test()
{
    Write("Test"); //Writing to what? Is it part of this class? 
    
    var result = Log(1); //Are we logging something? Is it it part of this class
    
    Clear(); //What are we clearing? If this class was some collection it will definitely be confusing.
             //Is it part of this class?
}

vs

public void Test()
{
    Console.Write("Test");
    
    var result = Math.Log(1); 
    
    Console.Clear();
}

@Sheldonfrith
Copy link

Do people not use modern IDE's while editing C# code? If you see "Write" for example and don't know what it means or where it comes from... just hover your mouse over it or right click and "go to definition".... I am very much in support of the feature proposed by the OP.

@theunrepentantgeek
Copy link

I shouldn't need to rely on a fancy IDE to understand code that's already been written.

Your experience may differ, but I read a lot of code in my browser - code reviews on GitHub & Azure DevOps, reading blog posts, doing training, searching documentation, and so on, where most of the usual IDE tooling is unavailable.

Also, there are C# developers who use simple text editors, for a number of reasons. Some choose to work that way, some have no choice due to corporate policies. Should they be further disadvantaged simply because we have IDEs?

@CyrusNajmabadi
Copy link
Member

@theunrepentantgeek I don't see how you are disadvantaged. The meaning of code in c# had always been affected by other files. It has never been our aim that you can look at a snippet of code out of context and know precisely what it means.

Heck, just consider the most basic operator: .

This could mean so many things, and can change meaning depending on other files or references.

It's the nature of the language.

@theunrepentantgeek
Copy link

I totally agree with you @CyrusNajmabadi; I'm lucky enough to have Visual Studio and/or Visual Studio code available when writing C# code.

I'm pushing back on @Sheldonfrith's strong implication that a "modern IDE" with tooltips and/or "go to definition" will always be available when reading code.

@CyrusNajmabadi
Copy link
Member

Well, I agree with him in that the only real way to understand is to either examine all the code, or have a rich ide. If you don't do that, then code may be more difficult to understand and know exactly what it means. This is just the nature of c# (and many other languages). This feature fits into that nature.

@333fred 333fred added the Implemented Needs ECMA Spec This feature has been implemented in C#, but still needs to be merged into the ECMA specification label Jan 11, 2022
@Shadowblitz16
Copy link

Please don't make this global make it act like it's defined in the namespace...

//File 1
namespace A;
global using i32 = int
//File 2
using A;
i32 = 0; // int under the hood
//File3
//using A;
i32 = 0; // error i32 doesn't exist since we didn't use A.

@333fred
Copy link
Member

333fred commented Jul 13, 2022

@Shadowblitz16 this shipped about 6 months ago. You cannot define global usings inside a namespace.

@Shadowblitz16
Copy link

@333fred so basically the C# team partially solved the typedef problem and potentially added global problems down the road.

@CyrusNajmabadi
Copy link
Member

basically the C# team partially solved the typedef problem

@Shadowblitz16 global usings are not typedefs. They're simply a shorthand for writing that using in each file.

and potentially added global problems down the road.

As above, it's equivalent to writing it out by hand. So you'll have the same problems you would adding this using to each file.

@dsaf
Copy link

dsaf commented Aug 13, 2022

Generic types don't seem to be well-supported #1239.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature Request Implemented Needs ECMA Spec This feature has been implemented in C#, but still needs to be merged into the ECMA specification Proposal champion Proposal
Projects
None yet
Development

No branches or pull requests