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] Syntactic sugar: allow "pseudo-types" for is-operator #12212

Closed
Unknown6656 opened this issue Jun 26, 2016 · 7 comments
Closed

[Proposal] Syntactic sugar: allow "pseudo-types" for is-operator #12212

Unknown6656 opened this issue Jun 26, 2016 · 7 comments

Comments

@Unknown6656
Copy link

Unknown6656 commented Jun 26, 2016

I would like to propose more "pseudo-types" for the is-operator, as I (and many other developers) often work with reflection and System::Type and have wondered, whether one could optimize code readabilty in such situations.

dynamic obj = "Hello, World!";

bool test0 = obj is string; //true (this already exists)

//new syntax proposals:
bool test1 = obj is class; //true, as the type is a managed class
bool test2 = obj is struct; //false, as the type is not a structure
bool test3 = obj is internal; //false, the type is publicly accessible
bool test4 = obj is public; //true
bool test5 = obj is <>; //false, as the type is not generic
bool test6 = obj is abstract; //false
bool test7 = obj is [Serializable]; //true, as the Serializable-Attribute is added to the type definition
bool test8 = obj is [ComVisible]; //true, as the ComVisible-Attribute is added to the type definition

//The previous two tests could be also combined as follows:
bool test9 = obj is [Serializable, ComVisible];

Assert.Equals(test7 & test8, test9); //true

As you can probably see, I would like to see different keywords/accessors/etc. being used with the is-operator, instead of using the circumbendibus syntax with with System::Type:

dynamic obj = "Hello, World!";
Type t = obj.GetType();

// etc.
bool test7 = t.IsSerializable;
bool test8 = t.GetCustomAttributes(typeof(ComVisibleAttribute), true).Count() > 0;

This proposal has multiple advantages:

  • No need for CLR-changes, as this syntax can be translated by the compiler
  • Much more readable and intuitive than the use of System::Type-instances

    (Especially for attribute and generic checks)
  • Cleaner and far mor shorter code

One could propose the following syntax rules for the is-Operator:

expression := expression "is" pattern

pattern := typename // `typename` means the existing .NET type name pattern
        |  typestring
        |  "<" commata ">"
        |  accessmodifier
        |  "[" attributes "]"

commata := "," commata
        |  __emptystring__

typestring := "Enum" //this already exists
           |  "class"
           |  "struct"
           |  "interface"
           |  "Delegate" //also exists

accessmodifier := accessmodifier "," accessmodifier
               |  "public"
               |  "private"
               |  "internal"
               |  "protected"
               |  "abstract"
               |  "sealed"
               //NOTE: accessmodifiers must not repeat or contradict themselves (e.g.: `private,public`)

attributes := attributes "," attributes
           |  attributename //the .NET name of the attribute (the name suffix `Attribute` is optional)

NOTE: The expression bool test = obj is <,,> would match any object instance obj, whoms type is a generic type with exactly three generic type arguments.

@HaloFour
Copy link

What happens in the case that obj is null? Do all of the above tests then fail?

Also, neither obj is abstract or obj is interface make much sense. The implementation of obj can't be either. Do you mean to test if obj happens to have an abstract base class, or if obj happens to implement any interface? What possible use case could that have?

Speaking of use cases, when would it use useful to know that obj happens to be a generic class of some specific arity? What can you do with that information?

@Unknown6656
Copy link
Author

@HaloFour: Well, you are right, that ... is interface or ... is abstract do not make sense...+

It would be better to implement something like this to test for multiple interfaces:

bool res = "hello" is IConvertible, IEnumerable; // true, as system::string can be cast to both interfaces

Concerning your null-question: The result should always be false (as the is-comparator would return in previous C# versions)

Concerning your last question, whether it would be useful: I have often used generic type creation or generic type pattern matching in the past, and I think that it could be rather useful.
One could also use this feature inside various .dotnet projects (e.g. various compilers or analysers which require pattern matching/analyzation)...

@bondsbw
Copy link

bondsbw commented Jul 1, 2016

While we're at it, perhaps comparisons directly on the type would also be useful:

void Foo<T>()
{
    //new syntax proposals:
    Console.WriteLine($"T is class: {T is class}");
    Console.WriteLine($"T is struct: {T is struct}");
    Console.WriteLine($"T is internal: {T is internal}");
    Console.WriteLine($"T is public: {T is public}");
    Console.WriteLine($"T is <>: {T is <>}");
    Console.WriteLine($"T is abstract: {T is abstract}");
    Console.WriteLine($"T is [Serializable]: {T is [Serializable]}");
    Console.WriteLine($"T is [ComVisible]: {T is [ComVisible]}");

    //The previous two tests could be also combined as follows:
    Console.WriteLine($"T is [Serializable, ComVisible]: {T is [Serializable, ComVisible]}");
}
Foo<string>();
// T is class: true
// T is struct: false
// T is internal: false
// T is public: true
// T is <>: false
// T is abstract: false
// T is [Serializable]: true
// T is [ComVisible]: true
// T is [Serializable, ComVisible]: true

Foo<int>();
// T is class: false
// T is struct: true
// T is internal: false
// T is public: true
// T is <>: false
// T is abstract: false
// T is [Serializable]: true
// T is [ComVisible]: true
// T is [Serializable, ComVisible]: true

@bondsbw
Copy link

bondsbw commented Jul 1, 2016

Though now that I think about it, the syntax T : class might be more appropriate given similar usage for type parameters constraints. I don't know.

@Unknown6656
Copy link
Author

@bondsbw: Your last proposal could be an issue for readability in the following situation:

Type t = ....;
bool res = t : public ? t : abstract ? t : class : t : struct : t : [Serializable];

is identical to:

bool res = (t is public) ? ((t is abstract) ? (t is class) : (t is struct)) : (t is [Serializable]);

I think that using a colon (:) instead of is renders the code less readable (independent of bracket-usage)

@bondsbw
Copy link

bondsbw commented Jul 9, 2016

Good point. I can't think of a reason to prefer the colon syntax which justifies the confusion caused by what you wrote.

@gafter
Copy link
Member

gafter commented Aug 10, 2017

Issue moved to dotnet/csharplang #807 via ZenHub

@gafter gafter closed this as completed Aug 10, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants