-
Notifications
You must be signed in to change notification settings - Fork 64
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] GUID Literal #27
Comments
Great proposal. This would eliminate the need to have a string attribute when you really want a GUID. |
yep, like. |
Would that really be the best syntax to use? I'd think using another character other than I can write a date literal either as |
Certainly we could do this but why are GUID generally literals useful? If it's just validation of magic strings we can do that with analyzers or compiler diagnostics. |
@AnthonyDGreen We maybe able to make the compile-time constants. |
@scottdorman I'd forgotten about this proposal. <GuidAttribute([9ED54F84-A89D-4fcd-A854-44251E925F09])>
class Foo
const aGuid = [9ED54F84-A89D-4fcd-A854-44251E925F09]
end class @AnthonyDGreen is it possible to directly support Guid literal -> GuidAttribute? [9ED54F84-A89D-4fcd-A854-44251E925F09]
class Foo |
We can definitely do this and I don't think it would be expensive at all. What I really want to know is that such a feature would be frequently useful. Inspired by &H, &O, &B integral literals:
A little more explicit:
We'd want to use a prefix, rather than a suffix like (S, L, I in integers) so that tooling could switch into guid-mode as you're typing (or more likely pasting) instead of trying to first parse it as a large subtract expression and then seeing the G suffix at the end. I never see the P and X formats in the wild so I'd rather not worry about them for now. We could avoid the typing issues with a suffix by just making it a validated string:
The compiler would make sure the format was right but still treat it as a string. Maybe we could support target-type conversions to System.Guid though. The implementation would probably very closely follow what we do for System.Decimal, where there's a literal syntax that translates into a constructor call. Should probably use this one. |
@AdamSpeight2008 GuidAttribute would need a new constructor that takes a GUID or we'd need to special case it and basically convert the GUID back to a string when applied to an attribute. |
@anthony ... not really, the compiler serializes the constructor blob anyway. |
|
It may need a new attribute. <AttributeUsage(AttributeTargets.Assembly + AttributeTargets.Interface + AttributeTargets.Class
AttributeTargets.Enum + AttributeTargets.Struct + AttributeTargets.Delegate,
Inherited = false),
System.Runtime.InteropServices.ComVisible(true)]
public noninheritable class GuidAttribute : Attribute
public readonly Value As Guid
public sub New GuidAttribute(guid As Guid)
me.Value = guid;
end sub
end class |
Would be nice to have a prototype to put up on something like sharplab so we could try it out a see hey / nay. @AnthonyDGreen Potential having a
A quick survey of the .net framework source |
Very early stages of a Prototype |
@AdamSpeight2008 why the quotes? Surely it would be:
Although, hailing from the pre VB days of &H I would probably vote for:
and probably allow:
@terrajobst Kevin |
@KevinRansom At the moment I'm treating it as a proof of concept.
@terrajobst It really needs a |
I will yield to you and @AnthonyDGreen on syntax, but be aware .... I will be in his office pestering for &G blah because I am old and stuck in my ways.. Kevin |
I think other languages use a similar syntax for guids, so it should be easy to copy and paste in from text. |
Unless I'm mistaken, wouldn't that be a violation of the language specification (.NET, VB and C#)? Common Type System & Common Language Specification:
Visual Basic Language Specification:
Adding the ability to specify a GUID in an attribute would require more than just changes to VB. Perhaps for a more lightweight change, we could:
For example, this: Dim g As Guid = "{2C151561-BA1B-462A-AD04-ACB8E53D9F98}" Would be compiled to: Dim g As Guid = New Guid(&H2C151561, &HBA1BS, &H462AS, {&HAD, &H4, &HAC, &HB8, &HE5, &H3D, &H9F, &H98}) You'd lose the ability to use type inferencing, but I can't think of any cases where you'd create a
|
I don't think there's any value to be had modifying the GuidAttribute type because all code will continue to consume the data from that attribute as a string. To do otherwise would limit that code to only the bleeding edge of .NET Framework versions which no one would do. |
@AnthonyDGreen I'm considering the following structures for guid literals, they should be able to handle all of the Guid formats. They definitely need refining. <!--********************
- Guid Literal Tokens
**********************-->
<enumeration name="Guid_Format">
<description>Represents one of allowed Guid Formats.</description>
<lm-equiv name="GuidFormat"></lm-equiv>
<native-equiv name="guidFormat"></native-equiv>
<native-equiv name="GuidFormat.Opcodes"></native-equiv>
<enumerators>
<enumerator name="None" />
<enumerator name="B">
<description>B {00000000-0000-0000-0000-000000000000}</description>
</enumerator>
<enumerator name="D">
<description>D 00000000-0000-0000-0000-000000000000</description>
</enumerator>
<enumerator name="N">
<description>N 00000000000000000000000000000000</description>
</enumerator>
<enumerator name="P">
<description>P (00000000-0000-0000-0000-000000000000)</description>
</enumerator>
<enumerator name="X">
<description>X {0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}</description>
</enumerator>
</enumerators>
</enumeration>
<enumeration name="Guid_Seperator">
<description>Represents one of allowed Guid Seperators.</description>
<lm-equiv name="Guid_Seperator"></lm-equiv>
<native-equiv name="guid_Seperator"></native-equiv>
<native-equiv name="Guid_Seperator.Opcodes"></native-equiv>
<enumerators>
<enumerator name="None"/>
<enumerator name="Hypen">
<description>-</description>
</enumerator>
<enumerator name="Comma">
<description>,</description>
</enumerator>
</enumerators>
</enumeration>
<enumeration name="Guid_Opening">
<description>Represents one of allowed Guid Openings.</description>
<lm-equiv name="Guid_Seperator"></lm-equiv>
<native-equiv name="guid_Opening"></native-equiv>
<native-equiv name="Guid_Opening.Opcodes"></native-equiv>
<enumerators>
<enumerator name="None"/>
<enumerator name="Bo">
<description>{</description>
</enumerator>
<enumerator name="Po">
<description>(</description>
</enumerator>
</enumerators>
</enumeration>
<enumeration name="Guid_Closing">
<description>Represents one of allowed Guid Closing.</description>
<lm-equiv name="Guid_Seperator"></lm-equiv>
<native-equiv name="guid_Closing"></native-equiv>
<native-equiv name="Guid_Closing.Opcodes"></native-equiv>
<enumerators>
<enumerator name="None"/>
<enumerator name="Bc">
<description>}</description>
</enumerator>
<enumerator name="Pc">
<description>)</description>
</enumerator>
</enumerators>
</enumeration>
<node-structure name="Guid_PrefixSyntax" parent ="SyntaxToken">
<description>Represents the Guid literal prefix. (ie ~)</description>
<node-kind name="Guid_Prefix"/>
</node-structure>
<node-structure name="Guid_LiteralSyntax" parent="VisualBasicSyntaxNode">
<description>Represents the Guid literal. (ie ~)</description>
<node-kind name="Guid_Literal" />
<child name="Prefix" kind="Guid_Prefix" />
<child name="Guid" kind="Guid_Section" />
</node-structure>
<node-structure name="Guid_SectionSyntax" parent="VisualBasicSyntaxNode">
<description>Represents a guid section, consisting of format and optional block.</description>
<node-kind name="Guid_Section" />
<field name="Format" type="Guid_Format" />
<child name="Block" kind="Guid_Block" optional="true"/>
</node-structure>
<node-structure name="Guid_BlockSyntax" parent="VisualBasicSyntaxNode">
<description>Represents a guid block.</description>
<node-kind name="Guid_Block"/>
<field name="Opening" type="Guid_Opening" optional="true"/>
<child name="Contents" kind="Guid_BlockContent" list="true"/>
<field name="Closing" type="Guid_Closing" optional="true"/>
</node-structure>
<node-structure name="Guid_BlockContentSyntax" parent="VisualBasicSyntaxNode">
<description>Represents a guid block content.</description>
<node-kind name="Guid_BlockContent"/>
<child name="InnerBlock" kind="Guid_Block" optional="true"/>
<field name="Seperator" type="Guid_Seperator" optional="true"/>
<child name="HexDigits" kind="Guid_HexDigits" optional="true"/>
</node-structure>
<node-structure name="Guid_HexDigitsSyntax" parent="VisualBasicSyntaxNode" >
<description>Represents a Guid Hex Value. (ie 0xFEDC FEDC)</description>
<node-kind name="Guid_HexDigits"/>
<child name="Prefix" kind="Guid_HexPrefixToken" optional="true"/>
<child name="Digits" kind="Guid_HexDigit" list="true"/>
</node-structure>
<node-structure name="Guid_HexDigitSyntax" parent="VisualBasicSyntaxNode">
<description>Represents a Guid Hex digit. (ie 0-9A-Fa-f)</description>
<node-kind name="Guid_HexDigit"/>
<child name="Value" kind="EmptyToken"/>
</node-structure>
<node-structure name="Guid_HexPrefixToken" parent="SyntaxToken">
<description>Represents the Guid Hex Prefix. (ie 0x)</description>
<node-kind name="Guid_HexPrefixToken"/>
</node-structure> |
Seems like a lot of structure for a literal. Why do GUIDs require so much more structure than say, Date literals? |
That constructor wouldn't help because there is no encoding at the ECMA 335 metadata level to persist an instance of the |
@terrajobst It could be stored as |
I maintain that it doesn't matter if we added such a constructor because no consumer of the attribute would ever take advantage of it. Additionally we don't have any data to suggest that the cost of reparsing the GUID at runtime is consequential. So, it looks like the only value of this feature is compile-time validation of a string to match a particular format. That's still valuable, but I think it's not worth all the compiler magic suggested. At this point I think a more general solution would be some kind of annotation on a string that makes it easier for analyzers to target and validate string literals against some format, e.g.: Straw-man syntax ? "2017-09-25"Date
? "0f8fad5b-d9cb-469f-a165-70867728950e"GUID
? "{'key': value}"JSON
? ".*"RegEx
? "{1}, {0}"FormatString
? "867-5309"PhoneNumber Such annotated literals would be still be strings and behave in all ways like strings and work with all existing APIs, would still be compile-time constants that work in attributes, etc., but would be easily marked for validation by one or more analyzers. |
Agree it should be handled just like date literals are handled. |
As for usefulness, we currently have a file containing more than 100 GUIDs. We use GUIDs all over the place to identify just about everything in our system. I'm constantly writing LINQ queries to search for objects by their GUIDs. It's annoying to always have to write 'where p.UID == new Guid("xxxx-xxxx-etc")' and there are many cases where we place attributes on stuff to indicate the GUID of the thing that this code maps to. I want this also to be a C# proposal, along with all the CLR changes needed for this to work without it just being syntactic sugar over "new Guid". |
@mkane91301 aside from the annoyance of constructing the GUID could you elaborate on the difficulties you're having today working with them? You didn't mention any performance bottlenecks with parsing nor runtime errors from mistyped GUIDs. Why is syntactic sugar over |
I don't think integrating GUIDs into the language will help with Linq queries. In the end, you still have to tell the system how to find the GUID off of a domain type. Even without language integration you can trivially create some syntactic sugar for yourself today: // I assume you already have something like this:
//
// public interface IUnqiueObject
// {
// Guid UID { get; }
// }
public static class GuidExtensions
{
public static T SingleOrDefault<T>(this IEnumerable<T> source, Guid guid) where T: IUnqiueObject
{
return SingleOrDefault.Where(o => o.UID == guid);
}
public static T Single<T>(this IEnumerable<T> source, Guid guid) where T: IUnqiueObject
{
return Single.Where(o => o.UID == guid);
}
}
// Usage
var myOrder = orders.Single(guid); (Thank god you said C# otherwise I would feel guilty for not providing this sample in VB 😄 ) |
You both should feel guilty for polluting this sacred space with semicolons ;) @Bill-McC I'm liking your tagging syntax! |
The idea of tagging strings seems reasonable to me too. Low overhead in the grammar while retaining the value prop of them being validated. And, as @AnthonyDGreen pointed out, easy to extend to allow other kinds of formats in the future. Making instances of the |
If these are static GUIDs that you find yourself referencing all over the place, in either attributes or code, then you can already easily add them as string constants with corresponding Public Module Guids
Public Const ProductGuidName = "xxxx-xxxx-etc"
Public Readonly ProductGuid As Guid = New Guid(ProductGuidName)
End Module
' Usage in attributes
<Guid(Guids.ProductGuidName)> _
Public Class Product : Inherits BaseClass
End Class
' Usage in LINQ
Dim myProducts = things.Where(Function (thing) thing.Guid == Guids.ProductGuid) Obviously those constants could still accidentally be invalid GUIDs and that's where this proposed validator tag could come in real handy. |
Agreed. One could slightly extend the rules to allow tagged literals to be target typed to the Public Module Guids
Public Readonly ProductGuid As Guid = "xxxx-xxxx-etc"GUID
End Module |
I'm pretty sure we've waded into C++ custom literals. 😁 |
@HaloFour and that, together with strong type aliases, is a very-very good thing! |
Seems to me VB has jumped the shark when they added XML literals. So at this point, any literals no mater how convoluted the syntax seems fair game ;-). Hey @AnthonyDGreen if you guys add support for inline C# I might use VB more often |
Nonsense, @terrajobst . XML literals don't create some slippery slope that can justify adding anything that comes to mind to the language. On an unrelated note, have you looked at #101 - so exciting! |
Besides, C# did it first (well, Cω technically): https://msdn.microsoft.com/en-us/library/ms974195.aspx using Microsoft.Comega;
using System;
public class NewsItem{
attribute string title;
attribute string author;
struct { DateTime date; string body; }
public static void Main(){
NewsItem news = <NewsItem title="Hello World" author="Dare Obasanjo">
<date>{DateTime.Now}</date>
<body>I am the first post of the New Year.</body>
</NewsItem>;
Console.WriteLine(news.title + " by " + news.author + " on " + news.date);
}
} Wait, what were we talking about again? |
C-omega may have prototyped it, but VB perfected it :) |
@AnthonyDGreen if the tagging syntax was for any constant expression when you type the # intellisense could list the constant types. Would provide a nice experience rather than having to remember literals. E.g 42#Byte |
Pinging this as per comment on #213 above. Please give this some consideration in this latest round of reviews. We use GUIDs everywhere and having them as first-class citizens without resorting to wrapping them as strings would be helpful. As an aside, for a DSL for a project (using Irony.NetCore with VB), one of the first things I added was a GUID literal expression:
In VB, I would use GUID literals for attributes (at present wrapping them as strings) as well as any known magic immutables. Compile-time checking that the thing is actually a GUID is another benefit. |
(Ported from Roslyn Repo)
When guid are used their value tends not to change, throughout it's usage.
If we make it a compile-time constant,
Guid.Parse currently supports 5 different formats.
These format should also be supported by any GUID literal.
Proposed Literal Syntax (VB)
We could extend VB's date time literal syntax
#2016/06/01#
to cover GUIDs.In an Attribute
The text was updated successfully, but these errors were encountered: