{"id":22065633,"url":"https://github.com/karenpayneoregon/deconstructing-samples","last_synced_at":"2026-06-19T18:32:13.000Z","repository":{"id":56758397,"uuid":"451266064","full_name":"karenpayneoregon/deconstructing-samples","owner":"karenpayneoregon","description":"Basic to advance Deconstruct code samples","archived":false,"fork":false,"pushed_at":"2025-01-23T16:09:17.000Z","size":613,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-20T19:46:34.739Z","etag":null,"topics":["csharp-core","deconstructor","entity-framework-core","extension-methods"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/karenpayneoregon.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-01-24T00:18:45.000Z","updated_at":"2025-01-23T16:09:21.000Z","dependencies_parsed_at":"2025-01-12T11:19:19.168Z","dependency_job_id":"0f43a596-4c21-4ce7-9c91-27fedbde0786","html_url":"https://github.com/karenpayneoregon/deconstructing-samples","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/karenpayneoregon/deconstructing-samples","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karenpayneoregon%2Fdeconstructing-samples","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karenpayneoregon%2Fdeconstructing-samples/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karenpayneoregon%2Fdeconstructing-samples/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karenpayneoregon%2Fdeconstructing-samples/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/karenpayneoregon","download_url":"https://codeload.github.com/karenpayneoregon/deconstructing-samples/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karenpayneoregon%2Fdeconstructing-samples/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34544403,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-19T02:00:06.005Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp-core","deconstructor","entity-framework-core","extension-methods"],"created_at":"2024-11-30T19:20:58.590Z","updated_at":"2026-06-19T18:32:12.983Z","avatar_url":"https://github.com/karenpayneoregon.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"﻿![img](assets/csharpDeconstructing.png)\n\n# Overview\n\nIn this article with code samples provides additional ways to return data from methods, iterating foreach statements, how to work with date time objects to have cleaner code. An important consideration is every developer has their own style of coding and with that keep an open mind, if something learned looks inviting but not your style than modify the code to suite you’re or a team’s style.\n\n\u003e **Note**\n\u003e Code was originally written using .NET 5 and has been updated to .NET 7. The only trick for this was to update EF Core 5 to EF Core 7 else some code would not function which was expected.\n\n## The art of Deconstructing\n\nWhat is deconstruct in C#?\n\nDeconstruction is a process of splitting a variable value into parts and storing them into new variables. \n\nThis could be useful when a variable stores multiple values such as a [Tuple](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-tuples) and different ways to work with `foreach` statements or create language extensions for returning smaller sets of information from third party methods.\n\nMost developers know about how to return informaton with Tuples but many don't know about other benefits which will be gone over in this article.\n\n**Microsoft documentation** Deconstructing tuples and other types [![](assets/Link_16x.png)](https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/deconstruct)\n\n## Warm and fuzzy\n\nPretty much at one time or another a developer needs to break apart a DateTime or DateTimeOffset as shown below.\n\n```csharp\nvar date = new DateTimeOffset(Now.Year, Now.Month, Now.Day, 0, 0, 0, 0, TimeSpan.Zero);\nint year = date.Year;\n```\n\nWe can deconstruct a DateTime or a DateTimeOffset using an extension method.\n\n```csharp\npublic static class Extensions\n{\n    public static void Deconstruct(this DateTimeOffset date, out int day, out int month, out int year) \n        =\u003e (day, month, year) = (date.Day, date.Month, date.Year);\n}\n```\n\nExample usage\n\n```csharp\nvar date = new DateTimeOffset(Now.Year, Now.Month, Now.Day, 0, 0, 0, 0, TimeSpan.Zero);\n\ndate.Deconstruct(out int day, out int month, out int year);\n\nConsole.WriteLine($\"Year: {year} Month: {month} Day: {day}\");\n```\n\nHow about a `DateOnly`? Yes we can do this also along with `TimeOnly`\n\n```csharp\npublic static void Deconstruct(this DateOnly date, out int day, out int month, out int year) =\u003e\n    (day, month, year) = (date.Day, date.Month, date.Year);\n```\n\nThis means a developer who works with dates can have cleaner code. But wait, why not a language extension method like below? Because this article focues on deconstructing :wink:\n\n```csharp\npublic static class Extensions\n{\n    public static (int day, int month, int year) Chunk(this DateTimeOffset sender) \n        =\u003e (sender.Day, sender.Month, sender.Year);\n}\n```\n\n## Basics\n\nThe following shows different ways to return data from a method.\n\nIn conventional coding returning information typically looks like the following which is devoid of any exception handling.\n\n```csharp\npublic class DataOperations\n{\n    public static async Task\u003cList\u003cCustomers\u003e\u003e GetCustomersList()\n    {\n        await using NorthContext context = new();\n        return await context\n            .Customers\n            .Include(c =\u003e c.ContactTypeIdentifierNavigation)\n            .ToListAsync();\n    }\n}\n```\n\nBut we all know that at anytime an exception may be thrown, in the above case perhaps the server where the database resides is not available.\n\nSo what can be done? Write to a log file and return `null` when an exception is thrown.\n\n```csharp\npublic class DataOperations\n{\n    public static async Task\u003cList\u003cCustomers\u003e\u003e GetCustomersList()\n    {\n        try\n        {\n            await using NorthContext context = new();\n            return await context\n                .Customers\n                .Include(c =\u003e c.ContactTypeIdentifierNavigation)\n                .ToListAsync();\n        }\n        catch (Exception ex)\n        {\n            // write to error log\n            return null;\n        }\n    }\n}\n```\n\nThe above although works is not a nice solution, let's try using deconstruction.\n\n```csharp\npublic class DataOperations\n{\n    public static async Task\u003c(List\u003cCustomers\u003e customers, Exception exception)\u003e GetCustomersList()\n    {\n        try\n        {\n            await using NorthContext context = new();\n            return \n                (\n                    await context.\n                        Customers\n                        .Include(c =\u003e c.ContactTypeIdentifierNavigation)\n                        .ToListAsync(), \n                    null\n                );\n        }\n        catch (Exception ex)\n        {\n            // write to error log\n            return (null, ex);\n        }\n    }\n}\n```\n\nUsage\n\n```csharp\nvar (customers, exception) = await DataOperations.GetCustomersList();\nif (exception is null)\n{\n    // use customers\n}\nelse\n{\n    // we have an exception object to see what happened\n}\n```\n\nIn this case we have returned either a list of customers and null or null and an exception.\n\nCan we do better? Yes and no, it depends on your programming style. Here an the first value is a bool which represents success or failure.\n\n```csharp\npublic class DataOperations\n{\n    public static async Task\u003c(bool success, List\u003cCustomers\u003e customers, Exception exception)\u003e GetCustomersList()\n    {\n        try\n        {\n            await using NorthContext context = new();\n            return \n                (\n                    true,\n                    await context.\n                        Customers\n                        .Include(c =\u003e c.ContactTypeIdentifierNavigation)\n                        .ToListAsync(), \n                    null\n                );\n        }\n        catch (Exception ex)\n        {\n            // write to error log\n            return (false,null, ex);\n        }\n    }\n}\n```\n\nPersonally, it's clearer adding the bool.\n\n```csharp\nvar (success, customers, exception) = await DataOperations.GetCustomersList();\nif (success)\n{\n    // use customers\n}\nelse\n{\n    // check out exception\n}\n```\n\nEach of the above examples perform the same operations with varying ways to return data. The first should be avoided unless it’s never going to fail. To be honest, the second is what an uneducated coder might come up with and is not recommended. The final two are personal choices along with showing we can return information and name them like local variables.\n\nSuppose we want to simply check success and not access the exception in regards to the last example? We can use a [discard](https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/discards).\n\n![x](assets/discard.png)\n\n\n## Deconstructing non-tuples for classes/models \n\nThis feature is great, but it is actually not limited to just tuples - you can add deconstructors to all your classes. Using the following syntax we can return and deconstruct only the information needed for an operation. The benefit is not needing to return all properties of a model and that there may be several different operations needing different sets of data.\n\n```csharp\npublic class PersonEntity\n{\n    public int PersonID { get; set; }\n    public string FirstName { get; set; }\n    public string LastName { get; set; }\n    public string FullName =\u003e $\"{FirstName} {LastName}\";\n    public ICollection\u003cStudentGrade\u003e Grades { get; set; }\n    public override string ToString() =\u003e $\"{FirstName} {LastName}\";\n\n    public void Deconstruct(out int id, out string firstName, out string lastName)\n    {\n        id = PersonID;\n        firstName = FirstName;\n        lastName = LastName;\n    }\n    public void Deconstruct(out int id, out string fullName)\n    {\n        id = PersonID;\n        fullName = FullName;\n    }\n    public void Deconstruct(out int id, out string firstName, string lastName, ICollection\u003cStudentGrade\u003e grades)\n    {\n        id = PersonID;\n        firstName = FirstName;\n        lastName = LastName;\n        grades =Grades;\n    }\n}\n```\n\nUsage using a discard which means we don't want first name, only the primary key and last name.\n\n```csharp\nvar (id, _, last) = (PersonEntity)SomeControl.SelectedItem;\n```\n\nCaveat, the above can only be done with classes you have access too. Suppose there is a class you don't have source code too? The solution is to create a language extension.\n\nHere is a model in a third party library\n\n```csharp\n\nnamespace SomeThirdPartyLibrary.Classes\n{\n    public class Customer\n    {\n        public int CustomerIdentifier { get; set; }\n        public string CompanyName { get; set; }\n        public int? ContactId { get; set; }\n        public string Street { get; set; }\n        public string City { get; set; }\n        public string PostalCode { get; set; }\n        public int? CountryIdentifier { get; set; }\n        public override string ToString() =\u003e CompanyName;\n    }\n}\n```\n\nIn your project we create an extension method for `Customer`.\n\n```csharp\nusing SomeThirdPartyLibrary.Classes;\n\nnamespace DeconstructCodeSamples.Extensions\n{\n    public static class ThirdPartyExtensions\n    {\n        /// \u003csummary\u003e\n        ///  Extension for third party class\n        /// \u003c/summary\u003e\n        /// \u003cparam name=\"customer\"\u003e\u003c/param\u003e\n        /// \u003cparam name=\"id\"\u003ecustomer key\u003c/param\u003e\n        /// \u003cparam name=\"companyName\"\u003ecustomer's company name\u003c/param\u003e\n        /// \u003cparam name=\"contactIdentifier\"\u003ecustomer's contact identifier\u003c/param\u003e\n        /// \u003cparam name=\"countryIdentifier\"\u003ecountry identifier for customer\u003c/param\u003e\n        public static void Deconstruct(this Customer customer, out int id, out string companyName, out int? contactIdentifier, out int? countryIdentifier)\n        {\n            id = customer.CustomerIdentifier;\n            companyName = customer.CompanyName;\n            contactIdentifier = customer.ContactId;\n            countryIdentifier = customer.CountryIdentifier;\n        }\n    }\n}\n```\n\n## Deconstucting other code\n\nLet's look at a simple dictionary\n\n```csharp\nvar peopleDictionary = new Dictionary\u003cstring, int\u003e\n{\n    [\"Mary\"] = 32, \n    [\"Frank\"] = 17\n};\n```\n\nConvental method to iterate the key and values.\n\n```csharp\nforeach (var pair in peopleDictionary)\n{\n    Console.WriteLine($\"{pair.Key} is {pair.Value} years old\");\n}\n```\n\nMinor issue when looking at the code is say the dictionary definition is not visible, it can be difficult to tell what key and value are, and in a little bit I will drive this home with a more complex example using a switch and grouping.\n\nNext level, deconstruct and provide meaningful variable names.\n\n```csharp\nforeach (var (name, age) in peopleDictionary)\n{\n    Console.WriteLine($\"{name} is {age} years old\");\n}\n```\n\nThe above is easy to understand. Now for those who like the cool factor here you go.\n\n```csharp\nforeach (var (name, age) in peopleDictionary.Select(x =\u003e (x.Key, x.Value)))\n{\n    Console.WriteLine($\"{name} is {age} years old.\");\n}\n```\n\n:question: Which to use? the second example, it's clear and easy to read.\n\n## Dictionary/IGrouping deconstruct\n\nIn this example the model where the task is to group by price, Cheap, Medium and Expensive.\n\n```csharp\npublic partial class Book\n{\n    public int Id { get; set; }\n    public string Title { get; set; }\n    public decimal? Price { get; set; }\n}\n```\n\nCode to read and group.\n\n```csharp\nvar books = await context.Book.ToListAsync();\n\nDictionary\u003cstring, IGrouping\u003cstring, Book\u003e\u003e results = books\n    .GroupBy(book =\u003e book.Price switch\n    {\n        \u003c= 10 =\u003e \"Cheap\",\n        \u003e 10 and \u003c= 20 =\u003e \"Medium\",\n        _ =\u003e \"Expensive\"\n    })\n    .ToDictionary(gb =\u003e\n            gb.Key,\n        g =\u003e g);\n```\n\nConventional `foreach` where like the prior conventional `foreach` we have is Key and Value which can make understand the code hard.\n\n```csharp\nforeach (KeyValuePair\u003cstring, IGrouping\u003cstring, Book\u003e\u003e pair in results)\n{\n    Console.WriteLine(pair.Key);\n    foreach (var book in pair.Value)\n    {\n        Console.WriteLine($\"\\t{book.Title,-25}{book.Price:C0}\");\n    }\n}\n```\n\nUsing Deconstruct the code is much easier to read.\n\n```csharp\nforeach (var (pricingCategory, bookGrouping) in results)\n{\n    Console.WriteLine(pricingCategory);\n    foreach (var book in bookGrouping)\n    {\n        Console.WriteLine($\"\\t{book.Title, -25}{book.Price:C0}\");\n    }\n}\n```\n\nAnd we can specify each variable type too.\n\n\n```csharp\nforeach ((string pricingCategory, IGrouping\u003cstring, Book\u003e bookGrouping) in results)\n{\n    Console.WriteLine(pricingCategory);\n    foreach (var book in bookGrouping)\n    {\n        Console.WriteLine($\"\\t{book.Title, -25}{book.Price:C0}\");\n    }\n}\n```\n\n## Records\n\nWe can deconstruct `records` also, given the following record `Deconstruct` provides us a way to return all information in a compact form.\n\n```csharp\npublic record Person()\n{\n    public int Id { get; init; }\n    public string Firstname { get; init; }\n    public string Lastname { get; init; }\n    public string FullName =\u003e $\"{Firstname} {Lastname}\";\n\n    public override string ToString() =\u003e $\"{Id} {FullName}\";\n\n    public void Deconstruct(out int id, out string fullName)\n        =\u003e (id, fullName) = (Id, FullName);\n\n}\n```\n\nUsage with mocked data using [Bogus](https://www.nuget.org/packages/Bogus) NuGet package.\n\n```csharp\nnamespace ForWritingArticle.Classes\n{\n    public class Operations\n    {\n        public static List\u003cPerson\u003e PeopleList(int count = 5)\n        {\n            var faker = new Faker\u003cPerson\u003e()\n                .WithRecord()\n                .RuleFor(p =\u003e p.Id, f =\u003e f.IndexVariable++)\n                .RuleFor(p =\u003e p.Lastname, f =\u003e f.Person.LastName)\n                .RuleFor(p =\u003e p.Firstname, f =\u003e f.Person.FirstName);\n\n            return faker.Generate(count);\n        }\n\n        public static void PeopleDeconstruct()\n        {\n            var list = PeopleList();\n\n            foreach (Person person in list)\n            {\n                var (id, fullName) = person;\n                Console.WriteLine($\"{id,-4}{fullName}\");\n            }\n\n        }\n    }\n}\n\n```\n\n---\n\n## Resharper\n\nGenerate Deconstructors `ReSharper_GenerateDeconstructor`\n\n**Before generation**\n```csharp\npublic class Version\n{\n    public int Major { get; }\n    public int Minor { get; }\n}\n```\n\n**After generation**\n```csharp\npublic class Version\n{\n    public int Major { get; }\n    public int Minor { get; }\n\n    public void Deconstruct(out int major, out int minor)\n    {\n        major = this.Major;\n        minor = this.Minor;\n    }\n}\n```\n\nIn the above example the developer may choice all properties or select only those they want.\n\nLike most of my Resharper shortcuts they have been changed from standard so I created a markdown file to remember them as from time to time I forget them but the following is available via a Visual Studio external tool.\n\n| Description        |   Shortcut    | Keyboard Reference\n|:------------- |:-------------|:-------------|\n|  Extract Interface| \u003ckbd\u003ectrl\u003c/kbd\u003e + \u003ckbd\u003eO\u003c/kbd\u003e \u003ckbd\u003eG\u003c/kbd\u003e| [ReSharper_ExtractInterface](https://www.jetbrains.com/help/resharper/Refactorings__Extract_Interface.html) |\n|  Extract Superclass| \u003ckbd\u003ectrl\u003c/kbd\u003e + \u003ckbd\u003eO\u003c/kbd\u003e \u003ckbd\u003eE\u003c/kbd\u003e |[ReSharper_ExtractSuperclass](https://www.jetbrains.com/help/resharper/Refactorings__Extract_Superclass.html) |\n|  Change Signature| \u003ckbd\u003ectrl\u003c/kbd\u003e + \u003ckbd\u003eO\u003c/kbd\u003e \u003ckbd\u003eS\u003c/kbd\u003e| [ReSharper_ChangeSignature](https://www.jetbrains.com/help/resharper/Refactorings__Change_Signature.html) |\n|  Convert Anonymous to Named Type refactoring| \u003ckbd\u003ectrl\u003c/kbd\u003e + \u003ckbd\u003eO\u003c/kbd\u003e \u003ckbd\u003eP\u003c/kbd\u003e | [ReSharper_Anonymous2Declared](https://www.jetbrains.com/help/resharper/Refactorings__Convert_Anonymous_to_Named_Type.html) |\n| Extract Method refactoring | \u003ckbd\u003ectrl\u003c/kbd\u003e + \u003ckbd\u003eO\u003c/kbd\u003e \u003ckbd\u003eI\u003c/kbd\u003e | [ReSharper_ExtractMethod](https://www.jetbrains.com/help/resharper/Refactorings__Extract_Method.html) |\n| Generate Deconstructors﻿ | \u003ckbd\u003ectrl\u003c/kbd\u003e + \u003ckbd\u003eT\u003c/kbd\u003e \u003ckbd\u003eY\u003c/kbd\u003e | [ReSharper_GenerateDeconstructor](https://www.jetbrains.com/help/resharper/Code_Generation_Deconstructors.html) |\n\n\n## Notes\n\n- If using a framework prior to 4.8, you will need the following NuGet package [System.ValueTuple](https://www.nuget.org/packages/System.ValueTuple), otherwise the package is included in new projects\n\n## Credits\n\n- DateTime extension Adam Storr [![](assets/Link_16x.png)](https://adamstorr.azurewebsites.net/blog/playing-with-csharp-7-deconstruct)\n\n## References\n\n- Quickstart guide for deconstructions (C# 7.0) [![](assets/Link_16x.png)](https://github.com/dotnet/roslyn/blob/main/docs/features/deconstruction.md)\n- C# 10: Mix declarations and expressions in a deconstruction [![](assets/Link_16x.png)](https://anthonygiretti.com/2021/07/23/introducing-c-10-mix-declarations-and-expressions-in-a-deconstruction/)\n- JetBrains Rider Generate Deconstructors [![](assets/Link_16x.png)](https://www.jetbrains.com/help/rider/Code_Generation_Deconstructors.html)\n- Using Tuples in C# to Initialize Properties in the Constructor and to Deconstruct Your Object [![](assets/Link_16x.png)](https://www.thomasclaudiushuber.com/2021/03/25/csharp-using-tuples-to-initialize-properties/)\n- Avoid C# 9 Record Gotchas [![](assets/Link_16x.png)](https://khalidabuhakmeh.com/avoid-csharp-9-record-gotchas)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarenpayneoregon%2Fdeconstructing-samples","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkarenpayneoregon%2Fdeconstructing-samples","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarenpayneoregon%2Fdeconstructing-samples/lists"}