Open
Description
Background and motivation
I maintain a library that provides an idiomatic API for both C# and F#. For example if a method accepts a delegate, it will have an overload that accepts an equivalent F# FSharpFunc
object. However both methods are visible in IntelliSense regardless of the language. It would be nice if the FSharpFunc
overload was visible only in F# projects and the delegate overload was visible to projects of all languages except F#. I propose an attribute to allow that.
API Proposal
namespace System.Runtime.CompilerServices;
[System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Constructor |
System.AttributeTargets.Delegate | System.AttributeTargets.Enum | System.AttributeTargets.Event |
System.AttributeTargets.Field | System.AttributeTargets.Interface | System.AttributeTargets.Method |
System.AttributeTargets.Property | System.AttributeTargets.Struct, AllowMultiple = true)]
public class LanguageVisibleAttribute : Attribute
{
public string Language { get; } // "C#", "F#" or "VB"
public bool IsVisible { get; }
public LanguageVisibleAttribute(string language, bool isVisible);
}
This attribute will override EditorBrowsableAttribute
for a project if Language
is the same with the project's language. To make a symbol visible in exclusively a set of languages, users have to also apply [EditorBrowsable(EditorBrowsableState.Never)]
to hide it from the other languages.
API Usage
public class MyClass
{
// This attribute controls the name the symbol will be available in F#.
// The most sensible method for each language will have the shortest name.
[Microsoft.FSharp.Core.CompilationSourceName("GetValueOrNull")]
public object? GetValue();
[Microsoft.FSharp.Core.CompilationSourceName("GetValue")]
[EditorBrowsable(EditorBrowsableState.Never), LanguageVisible("F#", true)] // Make it visible only in F#.
public FSharpValueOption<object> FSharpGetValue();
// Hide it only from F#. A delegate will make sense in any other language.
[LanguageVisible("F#", false)]
public void MyMethod(Func<int, int, int> f);
[EditorBrowsable(EditorBrowsableState.Never), LanguageVisible("F#", true)]
public void MyMethod(FSharpFunc<int, FSharpFunc<int, int>> f);
}
Alternative Designs
- Extending
EditorBrowsableAttribute
with aLanguage
property and allowing it to be applied multiple times is not a good idea for two reasons. First, existing tools will be confused if they see this attribute more than once, and second I would like this attribute to be specified in source and matched by name, to enable using it in earlier frameworks. - In my examples I used this attribute to special-case visibility only for F#. Since there are only two major and actively evolved languages in .NET, perhaps this attribute should be something F#-specific and maintained by the F# team, but a general solution seems better.
VB
could be renamed toVisual Basic
. The former is recognized bydotnet new
, and the latter is used by Roslyn'sLanguageNames
.
Risks
- There might be some confusion of the recognized values of the language string. Users that didn't read the documentation might apply
[LanguageVisible("FSharp", true)]
and they would not immediately realize they made a mistake. - Should we hide all symbols that are not supported in Visual Basic? I wouldn't say so, features like
ref struct
s are fundamentally not supported in Visual Basic and the IDEs can mechanically find and hide them if they desire.FSharpFunc
on the other hand is a regular class that could be used from C#; it just feels weird and the experience is not good.