Strongly typed Guid as generic struct











up vote
34
down vote

favorite
3












I already make twice same bug in code like following:



void Foo(Guid appId, Guid accountId, Guid paymentId, Guid whateverId)
{
...
}

Guid appId = ....;
Guid accountId = ...;
Guid paymentId = ...;
Guid whateverId =....;

//BUG - parameters are swapped - but compiler compiles it
Foo(appId, paymentId, accountId, whateverId);


OK, I want to prevent these bugs, so I created strongly typed GUIDs:



[ImmutableObject(true)]
public struct AppId
{
private readonly Guid _value;

public AppId(string value)
{
var val = Guid.Parse(value);
CheckValue(val);
_value = val;
}

public AppId(Guid value)
{
CheckValue(value);
_value = value;
}

private static void CheckValue(Guid value)
{
if(value == Guid.Empty)
throw new ArgumentException("Guid value cannot be empty", nameof(value));
}

public override string ToString()
{
return _value.ToString();
}
}


And another one for PaymentId:



[ImmutableObject(true)]
public struct PaymentId
{
private readonly Guid _value;

public PaymentId(string value)
{
var val = Guid.Parse(value);
CheckValue(val);
_value = val;
}

public PaymentId(Guid value)
{
CheckValue(value);
_value = value;
}

private static void CheckValue(Guid value)
{
if(value == Guid.Empty)
throw new ArgumentException("Guid value cannot be empty", nameof(value));
}

public override string ToString()
{
return _value.ToString();
}
}


These structs are almost same, there is a lot of duplication of code. Isn't is?



I cannot figure out any elegant way to solve it except using class instead of struct. I would rather use struct, because of null checks, less memory footprint, no garbage collector overhead etc...



Do you have some idea how to use struct without duplicating code?










share|improve this question




















  • 1




    Interesting need. I have a question. Leaving the code duplication aside, when you wanted to create one of those structs, you must initialize them with something. How do you make sure they are initialized with the right thing? As in..var appIdStruct=new AppId(paymentId) should not compile
    – bbeda
    Dec 12 at 17:55












  • I cannot guarantee it. I cannot check everything. Even then space for bugs will decrease a bit.
    – Tomas Kubes
    Dec 12 at 18:00






  • 1




    From that point of view, I think the space for bugs is the same, you are only moving the source to a different place. Also, you are complicating things which will actually increase that space long term. Are you planning to allocate lots of those? Will they live long in memory?
    – bbeda
    Dec 12 at 18:05












  • Not much, but null checks are also good reason for structs.
    – Tomas Kubes
    Dec 12 at 18:11















up vote
34
down vote

favorite
3












I already make twice same bug in code like following:



void Foo(Guid appId, Guid accountId, Guid paymentId, Guid whateverId)
{
...
}

Guid appId = ....;
Guid accountId = ...;
Guid paymentId = ...;
Guid whateverId =....;

//BUG - parameters are swapped - but compiler compiles it
Foo(appId, paymentId, accountId, whateverId);


OK, I want to prevent these bugs, so I created strongly typed GUIDs:



[ImmutableObject(true)]
public struct AppId
{
private readonly Guid _value;

public AppId(string value)
{
var val = Guid.Parse(value);
CheckValue(val);
_value = val;
}

public AppId(Guid value)
{
CheckValue(value);
_value = value;
}

private static void CheckValue(Guid value)
{
if(value == Guid.Empty)
throw new ArgumentException("Guid value cannot be empty", nameof(value));
}

public override string ToString()
{
return _value.ToString();
}
}


And another one for PaymentId:



[ImmutableObject(true)]
public struct PaymentId
{
private readonly Guid _value;

public PaymentId(string value)
{
var val = Guid.Parse(value);
CheckValue(val);
_value = val;
}

public PaymentId(Guid value)
{
CheckValue(value);
_value = value;
}

private static void CheckValue(Guid value)
{
if(value == Guid.Empty)
throw new ArgumentException("Guid value cannot be empty", nameof(value));
}

public override string ToString()
{
return _value.ToString();
}
}


These structs are almost same, there is a lot of duplication of code. Isn't is?



I cannot figure out any elegant way to solve it except using class instead of struct. I would rather use struct, because of null checks, less memory footprint, no garbage collector overhead etc...



Do you have some idea how to use struct without duplicating code?










share|improve this question




















  • 1




    Interesting need. I have a question. Leaving the code duplication aside, when you wanted to create one of those structs, you must initialize them with something. How do you make sure they are initialized with the right thing? As in..var appIdStruct=new AppId(paymentId) should not compile
    – bbeda
    Dec 12 at 17:55












  • I cannot guarantee it. I cannot check everything. Even then space for bugs will decrease a bit.
    – Tomas Kubes
    Dec 12 at 18:00






  • 1




    From that point of view, I think the space for bugs is the same, you are only moving the source to a different place. Also, you are complicating things which will actually increase that space long term. Are you planning to allocate lots of those? Will they live long in memory?
    – bbeda
    Dec 12 at 18:05












  • Not much, but null checks are also good reason for structs.
    – Tomas Kubes
    Dec 12 at 18:11













up vote
34
down vote

favorite
3









up vote
34
down vote

favorite
3






3





I already make twice same bug in code like following:



void Foo(Guid appId, Guid accountId, Guid paymentId, Guid whateverId)
{
...
}

Guid appId = ....;
Guid accountId = ...;
Guid paymentId = ...;
Guid whateverId =....;

//BUG - parameters are swapped - but compiler compiles it
Foo(appId, paymentId, accountId, whateverId);


OK, I want to prevent these bugs, so I created strongly typed GUIDs:



[ImmutableObject(true)]
public struct AppId
{
private readonly Guid _value;

public AppId(string value)
{
var val = Guid.Parse(value);
CheckValue(val);
_value = val;
}

public AppId(Guid value)
{
CheckValue(value);
_value = value;
}

private static void CheckValue(Guid value)
{
if(value == Guid.Empty)
throw new ArgumentException("Guid value cannot be empty", nameof(value));
}

public override string ToString()
{
return _value.ToString();
}
}


And another one for PaymentId:



[ImmutableObject(true)]
public struct PaymentId
{
private readonly Guid _value;

public PaymentId(string value)
{
var val = Guid.Parse(value);
CheckValue(val);
_value = val;
}

public PaymentId(Guid value)
{
CheckValue(value);
_value = value;
}

private static void CheckValue(Guid value)
{
if(value == Guid.Empty)
throw new ArgumentException("Guid value cannot be empty", nameof(value));
}

public override string ToString()
{
return _value.ToString();
}
}


These structs are almost same, there is a lot of duplication of code. Isn't is?



I cannot figure out any elegant way to solve it except using class instead of struct. I would rather use struct, because of null checks, less memory footprint, no garbage collector overhead etc...



Do you have some idea how to use struct without duplicating code?










share|improve this question















I already make twice same bug in code like following:



void Foo(Guid appId, Guid accountId, Guid paymentId, Guid whateverId)
{
...
}

Guid appId = ....;
Guid accountId = ...;
Guid paymentId = ...;
Guid whateverId =....;

//BUG - parameters are swapped - but compiler compiles it
Foo(appId, paymentId, accountId, whateverId);


OK, I want to prevent these bugs, so I created strongly typed GUIDs:



[ImmutableObject(true)]
public struct AppId
{
private readonly Guid _value;

public AppId(string value)
{
var val = Guid.Parse(value);
CheckValue(val);
_value = val;
}

public AppId(Guid value)
{
CheckValue(value);
_value = value;
}

private static void CheckValue(Guid value)
{
if(value == Guid.Empty)
throw new ArgumentException("Guid value cannot be empty", nameof(value));
}

public override string ToString()
{
return _value.ToString();
}
}


And another one for PaymentId:



[ImmutableObject(true)]
public struct PaymentId
{
private readonly Guid _value;

public PaymentId(string value)
{
var val = Guid.Parse(value);
CheckValue(val);
_value = val;
}

public PaymentId(Guid value)
{
CheckValue(value);
_value = value;
}

private static void CheckValue(Guid value)
{
if(value == Guid.Empty)
throw new ArgumentException("Guid value cannot be empty", nameof(value));
}

public override string ToString()
{
return _value.ToString();
}
}


These structs are almost same, there is a lot of duplication of code. Isn't is?



I cannot figure out any elegant way to solve it except using class instead of struct. I would rather use struct, because of null checks, less memory footprint, no garbage collector overhead etc...



Do you have some idea how to use struct without duplicating code?







c# generics struct guid






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Dec 12 at 18:01

























asked Dec 12 at 17:50









Tomas Kubes

12k1270102




12k1270102








  • 1




    Interesting need. I have a question. Leaving the code duplication aside, when you wanted to create one of those structs, you must initialize them with something. How do you make sure they are initialized with the right thing? As in..var appIdStruct=new AppId(paymentId) should not compile
    – bbeda
    Dec 12 at 17:55












  • I cannot guarantee it. I cannot check everything. Even then space for bugs will decrease a bit.
    – Tomas Kubes
    Dec 12 at 18:00






  • 1




    From that point of view, I think the space for bugs is the same, you are only moving the source to a different place. Also, you are complicating things which will actually increase that space long term. Are you planning to allocate lots of those? Will they live long in memory?
    – bbeda
    Dec 12 at 18:05












  • Not much, but null checks are also good reason for structs.
    – Tomas Kubes
    Dec 12 at 18:11














  • 1




    Interesting need. I have a question. Leaving the code duplication aside, when you wanted to create one of those structs, you must initialize them with something. How do you make sure they are initialized with the right thing? As in..var appIdStruct=new AppId(paymentId) should not compile
    – bbeda
    Dec 12 at 17:55












  • I cannot guarantee it. I cannot check everything. Even then space for bugs will decrease a bit.
    – Tomas Kubes
    Dec 12 at 18:00






  • 1




    From that point of view, I think the space for bugs is the same, you are only moving the source to a different place. Also, you are complicating things which will actually increase that space long term. Are you planning to allocate lots of those? Will they live long in memory?
    – bbeda
    Dec 12 at 18:05












  • Not much, but null checks are also good reason for structs.
    – Tomas Kubes
    Dec 12 at 18:11








1




1




Interesting need. I have a question. Leaving the code duplication aside, when you wanted to create one of those structs, you must initialize them with something. How do you make sure they are initialized with the right thing? As in..var appIdStruct=new AppId(paymentId) should not compile
– bbeda
Dec 12 at 17:55






Interesting need. I have a question. Leaving the code duplication aside, when you wanted to create one of those structs, you must initialize them with something. How do you make sure they are initialized with the right thing? As in..var appIdStruct=new AppId(paymentId) should not compile
– bbeda
Dec 12 at 17:55














I cannot guarantee it. I cannot check everything. Even then space for bugs will decrease a bit.
– Tomas Kubes
Dec 12 at 18:00




I cannot guarantee it. I cannot check everything. Even then space for bugs will decrease a bit.
– Tomas Kubes
Dec 12 at 18:00




1




1




From that point of view, I think the space for bugs is the same, you are only moving the source to a different place. Also, you are complicating things which will actually increase that space long term. Are you planning to allocate lots of those? Will they live long in memory?
– bbeda
Dec 12 at 18:05






From that point of view, I think the space for bugs is the same, you are only moving the source to a different place. Also, you are complicating things which will actually increase that space long term. Are you planning to allocate lots of those? Will they live long in memory?
– bbeda
Dec 12 at 18:05














Not much, but null checks are also good reason for structs.
– Tomas Kubes
Dec 12 at 18:11




Not much, but null checks are also good reason for structs.
– Tomas Kubes
Dec 12 at 18:11












3 Answers
3






active

oldest

votes

















up vote
41
down vote



accepted










First off, this is a really good idea. A brief aside:



I wish C# made it easier to create cheap typed wrappers around integers, strings, ids, and so on. We are very "string happy" and "integer happy" as programmers; lots of things are represented as strings and integers which could have more information tracked in the type system; we don't want to be assigning customer names to customer addresses. A while back I wrote a series of blog posts (never finished!) about writing a virtual machine in OCaml, and one of the best things I did was wrapped every integer in the virtual machine with a type that indicates its purpose. That prevented so many bugs! OCaml makes it very easy to create little wrapper types; C# does not.



Second, I would not worry too much about duplicating the code. It's mostly an easy copy-paste, and you are unlikely to edit the code much or make mistakes. Spend your time solving real problems. A little copy-pasted code is not a big deal.



If you do want to avoid the copy-pasted code, then I would suggest using generics like this:



struct App {}
struct Payment {}

[ImmutableObject(true)]
public struct Id<T>
{
private readonly Guid _value;
public Id(string value)
{
var val = Guid.Parse(value);
CheckValue(val);
_value = val;
}

public Id(Guid value)
{
CheckValue(value);
_value = value;
}

private static void CheckValue(Guid value)
{
if(value == Guid.Empty)
throw new ArgumentException("Guid value cannot be empty", nameof(value));
}

public override string ToString()
{
return _value.ToString();
}
}


And now you're done. You have types Id<App> and Id<Payment> instead of AppId and PaymentId, but you still cannot assign an Id<App> to Id<Payment> or Guid.



Also, if you like using AppId and PaymentId then at the top of your file you can say



using AppId = MyNamespace.Whatever.Id<MyNamespace.Whatever.App>


and so on.



Third, you will probably need a few more features in your type; I assume this is not done yet. For example, you'll probably need equality, so that you can check to see if two ids are the same.



Fourth, be aware that default(Id<App>) still gives you an "empty guid" identifier, so your attempt to prevent that does not actually work; it will still be possible to create one. There is not really a good way around that.






share|improve this answer























  • "There is not really a good way around that." Is there a bad way?
    – BurnsBA
    Dec 12 at 18:56






  • 2




    @BurnsBA: If it hurts when you do that, don't do it! :-)
    – Eric Lippert
    Dec 12 at 19:38






  • 1




    Do you think it would be useful to have a conversion to Guid defined for the type, or to have a public read-only property exposing the Guid? This would be useful for interfacing persistence frameworks, where the “raw” Guid is required.
    – dasblinkenlight
    Dec 13 at 3:02






  • 2




    @dasblinkenlight: That seems like a reasonable choice. Maybe an explicit conversion to and from Guid would be useful.
    – Eric Lippert
    Dec 13 at 3:28






  • 2




    A small interesting piece of information: A type that is used in the way that T is in this code is called a phantom type in the context of functional programming: It is used as a type parameter, but there is never a value of type T.
    – Lii
    2 days ago


















up vote
2
down vote













We do the same, it works great.



Yes, it's a lot of copy and paste, but that is exactly what code-generation is for.



In Visual Studio, you can use T4 templates for this. You basically write your class once and then have a template where you say "I want this class for App, Payment, Account,..." and Visual Studio will generate you one source code file for each.



That way you have one single source (The T4 template) where you can make changes if you find a bug in your classes and it will propagate to all your Identifiers without you having to think about changing all of them.






share|improve this answer




























    up vote
    -6
    down vote













    You might be able to use subclassing with a different programming language.






    share|improve this answer























    • All structure types are required to inherit from ValueType and nothing else.
      – supercat
      yesterday










    • Oh. Could you use a class instead?
      – Solomon Ucko
      yesterday










    • @SolomonUcko classes can't inherit from struct.
      – Andrey
      yesterday










    • Could Guid be a class?
      – Solomon Ucko
      yesterday










    • If Guid is class, it would consume more memory, it would be managed by garbage collector and it would be nullable (null checks problems).
      – Tomas Kubes
      23 hours ago











    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53748675%2fstrongly-typed-guid-as-generic-struct%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    3 Answers
    3






    active

    oldest

    votes








    3 Answers
    3






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    41
    down vote



    accepted










    First off, this is a really good idea. A brief aside:



    I wish C# made it easier to create cheap typed wrappers around integers, strings, ids, and so on. We are very "string happy" and "integer happy" as programmers; lots of things are represented as strings and integers which could have more information tracked in the type system; we don't want to be assigning customer names to customer addresses. A while back I wrote a series of blog posts (never finished!) about writing a virtual machine in OCaml, and one of the best things I did was wrapped every integer in the virtual machine with a type that indicates its purpose. That prevented so many bugs! OCaml makes it very easy to create little wrapper types; C# does not.



    Second, I would not worry too much about duplicating the code. It's mostly an easy copy-paste, and you are unlikely to edit the code much or make mistakes. Spend your time solving real problems. A little copy-pasted code is not a big deal.



    If you do want to avoid the copy-pasted code, then I would suggest using generics like this:



    struct App {}
    struct Payment {}

    [ImmutableObject(true)]
    public struct Id<T>
    {
    private readonly Guid _value;
    public Id(string value)
    {
    var val = Guid.Parse(value);
    CheckValue(val);
    _value = val;
    }

    public Id(Guid value)
    {
    CheckValue(value);
    _value = value;
    }

    private static void CheckValue(Guid value)
    {
    if(value == Guid.Empty)
    throw new ArgumentException("Guid value cannot be empty", nameof(value));
    }

    public override string ToString()
    {
    return _value.ToString();
    }
    }


    And now you're done. You have types Id<App> and Id<Payment> instead of AppId and PaymentId, but you still cannot assign an Id<App> to Id<Payment> or Guid.



    Also, if you like using AppId and PaymentId then at the top of your file you can say



    using AppId = MyNamespace.Whatever.Id<MyNamespace.Whatever.App>


    and so on.



    Third, you will probably need a few more features in your type; I assume this is not done yet. For example, you'll probably need equality, so that you can check to see if two ids are the same.



    Fourth, be aware that default(Id<App>) still gives you an "empty guid" identifier, so your attempt to prevent that does not actually work; it will still be possible to create one. There is not really a good way around that.






    share|improve this answer























    • "There is not really a good way around that." Is there a bad way?
      – BurnsBA
      Dec 12 at 18:56






    • 2




      @BurnsBA: If it hurts when you do that, don't do it! :-)
      – Eric Lippert
      Dec 12 at 19:38






    • 1




      Do you think it would be useful to have a conversion to Guid defined for the type, or to have a public read-only property exposing the Guid? This would be useful for interfacing persistence frameworks, where the “raw” Guid is required.
      – dasblinkenlight
      Dec 13 at 3:02






    • 2




      @dasblinkenlight: That seems like a reasonable choice. Maybe an explicit conversion to and from Guid would be useful.
      – Eric Lippert
      Dec 13 at 3:28






    • 2




      A small interesting piece of information: A type that is used in the way that T is in this code is called a phantom type in the context of functional programming: It is used as a type parameter, but there is never a value of type T.
      – Lii
      2 days ago















    up vote
    41
    down vote



    accepted










    First off, this is a really good idea. A brief aside:



    I wish C# made it easier to create cheap typed wrappers around integers, strings, ids, and so on. We are very "string happy" and "integer happy" as programmers; lots of things are represented as strings and integers which could have more information tracked in the type system; we don't want to be assigning customer names to customer addresses. A while back I wrote a series of blog posts (never finished!) about writing a virtual machine in OCaml, and one of the best things I did was wrapped every integer in the virtual machine with a type that indicates its purpose. That prevented so many bugs! OCaml makes it very easy to create little wrapper types; C# does not.



    Second, I would not worry too much about duplicating the code. It's mostly an easy copy-paste, and you are unlikely to edit the code much or make mistakes. Spend your time solving real problems. A little copy-pasted code is not a big deal.



    If you do want to avoid the copy-pasted code, then I would suggest using generics like this:



    struct App {}
    struct Payment {}

    [ImmutableObject(true)]
    public struct Id<T>
    {
    private readonly Guid _value;
    public Id(string value)
    {
    var val = Guid.Parse(value);
    CheckValue(val);
    _value = val;
    }

    public Id(Guid value)
    {
    CheckValue(value);
    _value = value;
    }

    private static void CheckValue(Guid value)
    {
    if(value == Guid.Empty)
    throw new ArgumentException("Guid value cannot be empty", nameof(value));
    }

    public override string ToString()
    {
    return _value.ToString();
    }
    }


    And now you're done. You have types Id<App> and Id<Payment> instead of AppId and PaymentId, but you still cannot assign an Id<App> to Id<Payment> or Guid.



    Also, if you like using AppId and PaymentId then at the top of your file you can say



    using AppId = MyNamespace.Whatever.Id<MyNamespace.Whatever.App>


    and so on.



    Third, you will probably need a few more features in your type; I assume this is not done yet. For example, you'll probably need equality, so that you can check to see if two ids are the same.



    Fourth, be aware that default(Id<App>) still gives you an "empty guid" identifier, so your attempt to prevent that does not actually work; it will still be possible to create one. There is not really a good way around that.






    share|improve this answer























    • "There is not really a good way around that." Is there a bad way?
      – BurnsBA
      Dec 12 at 18:56






    • 2




      @BurnsBA: If it hurts when you do that, don't do it! :-)
      – Eric Lippert
      Dec 12 at 19:38






    • 1




      Do you think it would be useful to have a conversion to Guid defined for the type, or to have a public read-only property exposing the Guid? This would be useful for interfacing persistence frameworks, where the “raw” Guid is required.
      – dasblinkenlight
      Dec 13 at 3:02






    • 2




      @dasblinkenlight: That seems like a reasonable choice. Maybe an explicit conversion to and from Guid would be useful.
      – Eric Lippert
      Dec 13 at 3:28






    • 2




      A small interesting piece of information: A type that is used in the way that T is in this code is called a phantom type in the context of functional programming: It is used as a type parameter, but there is never a value of type T.
      – Lii
      2 days ago













    up vote
    41
    down vote



    accepted







    up vote
    41
    down vote



    accepted






    First off, this is a really good idea. A brief aside:



    I wish C# made it easier to create cheap typed wrappers around integers, strings, ids, and so on. We are very "string happy" and "integer happy" as programmers; lots of things are represented as strings and integers which could have more information tracked in the type system; we don't want to be assigning customer names to customer addresses. A while back I wrote a series of blog posts (never finished!) about writing a virtual machine in OCaml, and one of the best things I did was wrapped every integer in the virtual machine with a type that indicates its purpose. That prevented so many bugs! OCaml makes it very easy to create little wrapper types; C# does not.



    Second, I would not worry too much about duplicating the code. It's mostly an easy copy-paste, and you are unlikely to edit the code much or make mistakes. Spend your time solving real problems. A little copy-pasted code is not a big deal.



    If you do want to avoid the copy-pasted code, then I would suggest using generics like this:



    struct App {}
    struct Payment {}

    [ImmutableObject(true)]
    public struct Id<T>
    {
    private readonly Guid _value;
    public Id(string value)
    {
    var val = Guid.Parse(value);
    CheckValue(val);
    _value = val;
    }

    public Id(Guid value)
    {
    CheckValue(value);
    _value = value;
    }

    private static void CheckValue(Guid value)
    {
    if(value == Guid.Empty)
    throw new ArgumentException("Guid value cannot be empty", nameof(value));
    }

    public override string ToString()
    {
    return _value.ToString();
    }
    }


    And now you're done. You have types Id<App> and Id<Payment> instead of AppId and PaymentId, but you still cannot assign an Id<App> to Id<Payment> or Guid.



    Also, if you like using AppId and PaymentId then at the top of your file you can say



    using AppId = MyNamespace.Whatever.Id<MyNamespace.Whatever.App>


    and so on.



    Third, you will probably need a few more features in your type; I assume this is not done yet. For example, you'll probably need equality, so that you can check to see if two ids are the same.



    Fourth, be aware that default(Id<App>) still gives you an "empty guid" identifier, so your attempt to prevent that does not actually work; it will still be possible to create one. There is not really a good way around that.






    share|improve this answer














    First off, this is a really good idea. A brief aside:



    I wish C# made it easier to create cheap typed wrappers around integers, strings, ids, and so on. We are very "string happy" and "integer happy" as programmers; lots of things are represented as strings and integers which could have more information tracked in the type system; we don't want to be assigning customer names to customer addresses. A while back I wrote a series of blog posts (never finished!) about writing a virtual machine in OCaml, and one of the best things I did was wrapped every integer in the virtual machine with a type that indicates its purpose. That prevented so many bugs! OCaml makes it very easy to create little wrapper types; C# does not.



    Second, I would not worry too much about duplicating the code. It's mostly an easy copy-paste, and you are unlikely to edit the code much or make mistakes. Spend your time solving real problems. A little copy-pasted code is not a big deal.



    If you do want to avoid the copy-pasted code, then I would suggest using generics like this:



    struct App {}
    struct Payment {}

    [ImmutableObject(true)]
    public struct Id<T>
    {
    private readonly Guid _value;
    public Id(string value)
    {
    var val = Guid.Parse(value);
    CheckValue(val);
    _value = val;
    }

    public Id(Guid value)
    {
    CheckValue(value);
    _value = value;
    }

    private static void CheckValue(Guid value)
    {
    if(value == Guid.Empty)
    throw new ArgumentException("Guid value cannot be empty", nameof(value));
    }

    public override string ToString()
    {
    return _value.ToString();
    }
    }


    And now you're done. You have types Id<App> and Id<Payment> instead of AppId and PaymentId, but you still cannot assign an Id<App> to Id<Payment> or Guid.



    Also, if you like using AppId and PaymentId then at the top of your file you can say



    using AppId = MyNamespace.Whatever.Id<MyNamespace.Whatever.App>


    and so on.



    Third, you will probably need a few more features in your type; I assume this is not done yet. For example, you'll probably need equality, so that you can check to see if two ids are the same.



    Fourth, be aware that default(Id<App>) still gives you an "empty guid" identifier, so your attempt to prevent that does not actually work; it will still be possible to create one. There is not really a good way around that.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Dec 12 at 18:23

























    answered Dec 12 at 18:10









    Eric Lippert

    533k14510471929




    533k14510471929












    • "There is not really a good way around that." Is there a bad way?
      – BurnsBA
      Dec 12 at 18:56






    • 2




      @BurnsBA: If it hurts when you do that, don't do it! :-)
      – Eric Lippert
      Dec 12 at 19:38






    • 1




      Do you think it would be useful to have a conversion to Guid defined for the type, or to have a public read-only property exposing the Guid? This would be useful for interfacing persistence frameworks, where the “raw” Guid is required.
      – dasblinkenlight
      Dec 13 at 3:02






    • 2




      @dasblinkenlight: That seems like a reasonable choice. Maybe an explicit conversion to and from Guid would be useful.
      – Eric Lippert
      Dec 13 at 3:28






    • 2




      A small interesting piece of information: A type that is used in the way that T is in this code is called a phantom type in the context of functional programming: It is used as a type parameter, but there is never a value of type T.
      – Lii
      2 days ago


















    • "There is not really a good way around that." Is there a bad way?
      – BurnsBA
      Dec 12 at 18:56






    • 2




      @BurnsBA: If it hurts when you do that, don't do it! :-)
      – Eric Lippert
      Dec 12 at 19:38






    • 1




      Do you think it would be useful to have a conversion to Guid defined for the type, or to have a public read-only property exposing the Guid? This would be useful for interfacing persistence frameworks, where the “raw” Guid is required.
      – dasblinkenlight
      Dec 13 at 3:02






    • 2




      @dasblinkenlight: That seems like a reasonable choice. Maybe an explicit conversion to and from Guid would be useful.
      – Eric Lippert
      Dec 13 at 3:28






    • 2




      A small interesting piece of information: A type that is used in the way that T is in this code is called a phantom type in the context of functional programming: It is used as a type parameter, but there is never a value of type T.
      – Lii
      2 days ago
















    "There is not really a good way around that." Is there a bad way?
    – BurnsBA
    Dec 12 at 18:56




    "There is not really a good way around that." Is there a bad way?
    – BurnsBA
    Dec 12 at 18:56




    2




    2




    @BurnsBA: If it hurts when you do that, don't do it! :-)
    – Eric Lippert
    Dec 12 at 19:38




    @BurnsBA: If it hurts when you do that, don't do it! :-)
    – Eric Lippert
    Dec 12 at 19:38




    1




    1




    Do you think it would be useful to have a conversion to Guid defined for the type, or to have a public read-only property exposing the Guid? This would be useful for interfacing persistence frameworks, where the “raw” Guid is required.
    – dasblinkenlight
    Dec 13 at 3:02




    Do you think it would be useful to have a conversion to Guid defined for the type, or to have a public read-only property exposing the Guid? This would be useful for interfacing persistence frameworks, where the “raw” Guid is required.
    – dasblinkenlight
    Dec 13 at 3:02




    2




    2




    @dasblinkenlight: That seems like a reasonable choice. Maybe an explicit conversion to and from Guid would be useful.
    – Eric Lippert
    Dec 13 at 3:28




    @dasblinkenlight: That seems like a reasonable choice. Maybe an explicit conversion to and from Guid would be useful.
    – Eric Lippert
    Dec 13 at 3:28




    2




    2




    A small interesting piece of information: A type that is used in the way that T is in this code is called a phantom type in the context of functional programming: It is used as a type parameter, but there is never a value of type T.
    – Lii
    2 days ago




    A small interesting piece of information: A type that is used in the way that T is in this code is called a phantom type in the context of functional programming: It is used as a type parameter, but there is never a value of type T.
    – Lii
    2 days ago












    up vote
    2
    down vote













    We do the same, it works great.



    Yes, it's a lot of copy and paste, but that is exactly what code-generation is for.



    In Visual Studio, you can use T4 templates for this. You basically write your class once and then have a template where you say "I want this class for App, Payment, Account,..." and Visual Studio will generate you one source code file for each.



    That way you have one single source (The T4 template) where you can make changes if you find a bug in your classes and it will propagate to all your Identifiers without you having to think about changing all of them.






    share|improve this answer

























      up vote
      2
      down vote













      We do the same, it works great.



      Yes, it's a lot of copy and paste, but that is exactly what code-generation is for.



      In Visual Studio, you can use T4 templates for this. You basically write your class once and then have a template where you say "I want this class for App, Payment, Account,..." and Visual Studio will generate you one source code file for each.



      That way you have one single source (The T4 template) where you can make changes if you find a bug in your classes and it will propagate to all your Identifiers without you having to think about changing all of them.






      share|improve this answer























        up vote
        2
        down vote










        up vote
        2
        down vote









        We do the same, it works great.



        Yes, it's a lot of copy and paste, but that is exactly what code-generation is for.



        In Visual Studio, you can use T4 templates for this. You basically write your class once and then have a template where you say "I want this class for App, Payment, Account,..." and Visual Studio will generate you one source code file for each.



        That way you have one single source (The T4 template) where you can make changes if you find a bug in your classes and it will propagate to all your Identifiers without you having to think about changing all of them.






        share|improve this answer












        We do the same, it works great.



        Yes, it's a lot of copy and paste, but that is exactly what code-generation is for.



        In Visual Studio, you can use T4 templates for this. You basically write your class once and then have a template where you say "I want this class for App, Payment, Account,..." and Visual Studio will generate you one source code file for each.



        That way you have one single source (The T4 template) where you can make changes if you find a bug in your classes and it will propagate to all your Identifiers without you having to think about changing all of them.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered 20 hours ago









        nvoigt

        49.1k85492




        49.1k85492






















            up vote
            -6
            down vote













            You might be able to use subclassing with a different programming language.






            share|improve this answer























            • All structure types are required to inherit from ValueType and nothing else.
              – supercat
              yesterday










            • Oh. Could you use a class instead?
              – Solomon Ucko
              yesterday










            • @SolomonUcko classes can't inherit from struct.
              – Andrey
              yesterday










            • Could Guid be a class?
              – Solomon Ucko
              yesterday










            • If Guid is class, it would consume more memory, it would be managed by garbage collector and it would be nullable (null checks problems).
              – Tomas Kubes
              23 hours ago















            up vote
            -6
            down vote













            You might be able to use subclassing with a different programming language.






            share|improve this answer























            • All structure types are required to inherit from ValueType and nothing else.
              – supercat
              yesterday










            • Oh. Could you use a class instead?
              – Solomon Ucko
              yesterday










            • @SolomonUcko classes can't inherit from struct.
              – Andrey
              yesterday










            • Could Guid be a class?
              – Solomon Ucko
              yesterday










            • If Guid is class, it would consume more memory, it would be managed by garbage collector and it would be nullable (null checks problems).
              – Tomas Kubes
              23 hours ago













            up vote
            -6
            down vote










            up vote
            -6
            down vote









            You might be able to use subclassing with a different programming language.






            share|improve this answer














            You might be able to use subclassing with a different programming language.







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited 20 hours ago

























            answered yesterday









            Solomon Ucko

            603719




            603719












            • All structure types are required to inherit from ValueType and nothing else.
              – supercat
              yesterday










            • Oh. Could you use a class instead?
              – Solomon Ucko
              yesterday










            • @SolomonUcko classes can't inherit from struct.
              – Andrey
              yesterday










            • Could Guid be a class?
              – Solomon Ucko
              yesterday










            • If Guid is class, it would consume more memory, it would be managed by garbage collector and it would be nullable (null checks problems).
              – Tomas Kubes
              23 hours ago


















            • All structure types are required to inherit from ValueType and nothing else.
              – supercat
              yesterday










            • Oh. Could you use a class instead?
              – Solomon Ucko
              yesterday










            • @SolomonUcko classes can't inherit from struct.
              – Andrey
              yesterday










            • Could Guid be a class?
              – Solomon Ucko
              yesterday










            • If Guid is class, it would consume more memory, it would be managed by garbage collector and it would be nullable (null checks problems).
              – Tomas Kubes
              23 hours ago
















            All structure types are required to inherit from ValueType and nothing else.
            – supercat
            yesterday




            All structure types are required to inherit from ValueType and nothing else.
            – supercat
            yesterday












            Oh. Could you use a class instead?
            – Solomon Ucko
            yesterday




            Oh. Could you use a class instead?
            – Solomon Ucko
            yesterday












            @SolomonUcko classes can't inherit from struct.
            – Andrey
            yesterday




            @SolomonUcko classes can't inherit from struct.
            – Andrey
            yesterday












            Could Guid be a class?
            – Solomon Ucko
            yesterday




            Could Guid be a class?
            – Solomon Ucko
            yesterday












            If Guid is class, it would consume more memory, it would be managed by garbage collector and it would be nullable (null checks problems).
            – Tomas Kubes
            23 hours ago




            If Guid is class, it would consume more memory, it would be managed by garbage collector and it would be nullable (null checks problems).
            – Tomas Kubes
            23 hours ago


















            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53748675%2fstrongly-typed-guid-as-generic-struct%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Список кардиналов, возведённых папой римским Каликстом III

            Deduzione

            Mysql.sock missing - “Can't connect to local MySQL server through socket”