Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Deconstruction in C# 7

DZone's Guide to

Deconstruction in C# 7

In this post, a web developer shows how we can use different methods in C# 7 to consume tuples. Read on to learn how and see some sample code!

· Web Dev Zone ·
Free Resource

Deploying code to production can be filled with uncertainty. Reduce the risks, and deploy earlier and more often. Download this free guide to learn more. Brought to you in partnership with Rollbar.

The deconstruction feature can be used to consume tuples. Also, the deconstruction feature can be used for user-defined types in .NET but for that, you need to provide a Deconstruct method.

public void Deconstruct(out T1 x1, ..., out Tn xn) { ... }

You can provide multiple overloads of the Deconstruct method.

Deconstruction for Tuples

Note: The HasValue function that is used in the below examples returns a (bool isValid, int errorCode, string errorMessage) Tuple type.

A. Deconstructing Declaration

By deconstructing a tuple, you can assign its elements individually to fresh variables as shown below:

private static (bool isValid, int errorCode, string errorMessage) HasValue(string inputString) // tuple return type
{
//Some code
}

B. Deconstructing Assignment

By deconstructing a tuple, you can also assign its elements individually to existing variables as shown below:

public static bool IsValidInputStringWithLogging(string inputString, out int errorCode, out string errorMessage)
{
	bool isValid;
	(isValid, errorCode, errorMessage) = HasValue(inputString); // deconstructing assignment

	// Some code
}

C. Discards in Deconstruction

C# 7 allows discards in deconstruction, you can ignore the elements of the tuple that you don't require.

Here, we don't require "errorCode" and "errorMessage", so we can discard them as shown below:

(bool isValid, _, _) = CSharp7Sample.HasValue(inputString); // deconstructing declaration with discard. Here, we don't require "errorCode" and "errorMessage"

Deconstruction for User-Defined Types

Note: The HasValue function used in the below examples returns OperationResult.

private static OperationResult HasValue(string inputString)
{
//Some code
}

A. Deconstructor Method

For User-Defined Types, you need to provide a Deconstruct method to use the deconstruction feature.

public class OperationResult
{
	public bool Status { get; set; }
	public int ErrorCode { get; set; }
	public string ErrorMessage { get; set; }

	public void Deconstruct(out bool status, out int errorCode, out string errorMessage)
	{
		status = this.Status;
		errorCode = this.ErrorCode;
		errorMessage = this.ErrorMessage;
	}
}

B. Deconstructor Method Overload

You can also provide multiple overloaded Deconstruct methods.

public class OperationResult
{
	public bool Status { get; set; }
	public int ErrorCode { get; set; }
	public string ErrorMessage { get; set; }

	public void Deconstruct(out bool status, out string logErrorMessage)
	{
		status = this.Status;
		logErrorMessage = $"{nameof(this.ErrorCode)}: {this.ErrorCode}, {nameof(this.ErrorMessage)}: {this.ErrorMessage}";
	}

	public void Deconstruct(out bool status, out int errorCode, out string errorMessage)
	{
		status = this.Status;
		errorCode = this.ErrorCode;
		errorMessage = this.ErrorMessage;
	}
}

C. Deconstructing Declaration

The below sample calls Deconstruct(out isValid, out errorCode, out errorMessage);

(bool isValid, int errorCode, string errorMessage) = HasValue(inputString); // deconstructing declaration
The below sample calls Deconstruct(out isValid, out logErrorMessage);

(bool isValid, string logErrorMessage) = CSharp7Sample.HasValue(inputString); // deconstructing declaration

D. Deconstructing Assignment

public static bool IsValidInputStringWithLogging(string inputString, out int errorCode, out string errorMessage)
{
	bool isValid;
	(isValid, errorCode, errorMessage) = HasValue(inputString); // deconstructing assignment
 
	// Some code
}

E. Discards in Deconstruction

C# 7 allows discards in deconstruction, you can ignore items returned by a Deconstruct method that you don't require.

(bool isValid, _, _) = HasValue(inputString); // deconstructing declaration with discard. Here, we don't require "errorCode" and "errorMessage"

In the following sections, you'll find full working sample code to demonstrate C# 7's Deconstruction feature. We'll look at deconstructing declarations, deconstructing assignments, deconstructor methods, and discards in deconstruction.

Full Working Sample Code to Explain the Use of Deconstruction for Tuples

The CSharp7Sample class contains an example I need to explain: deconstructing declaration, deconstructing assignment, and discards in deconstruction.

/// <summary>
///     CSharp7Sample.
/// </summary>
public partial class CSharp7Sample
{
	/// <summary>
	///     Checks Input String is valid (with logging).
	/// </summary>
	/// <param name="inputString">Input string.</param>
	/// <returns>true or false.</returns>
	public static bool IsValidInputStringWithLogging(string inputString)
	{
		(bool isValid, int errorCode, string errorMessage) = CSharp7Sample.HasValue(inputString); // deconstructing declaration

		if (isValid == false)
		{
			string logMessage = $"{nameof(inputString)}: {inputString}, {nameof(errorCode)}: {errorCode}, {nameof(errorMessage)}: {errorMessage}";
			Console.Write(logMessage);
		}

		return isValid;
	}

	/// <summary>
	///     Checks Input String is valid (with logging).
	/// </summary>
	/// <param name="inputString">Input string.</param>
	/// <returns>true or false.</returns>
	public static bool IsValidInputStringWithLogging(string inputString, out int errorCode, out string errorMessage)
	{
		bool isValid;
		(isValid, errorCode, errorMessage) = CSharp7Sample.HasValue(inputString); // deconstructing assignment

		if (isValid == false)
		{
			string logMessage = $"{nameof(inputString)}: {inputString}, {nameof(errorCode)}: {errorCode}, {nameof(errorMessage)}: {errorMessage}";
			Console.Write(logMessage);
		}

		return isValid;
	}

	/// <summary>
	///     Checks Input String is valid (without logging).
	/// </summary>
	/// <param name="inputString">Input string.</param>
	/// <returns>true or false.</returns>
	public static bool IsValidInputStringWithoutLogging(string inputString)
	{
		(bool isValid, _, _) = CSharp7Sample.HasValue(inputString); // deconstructing declaration with discard. Here, we don't require "errorCode" and "errorMessage"

		return isValid;
	}

	/// <summary>
	///     Input string has value or not.
	/// </summary>
	/// <param name="inputString">Input string.</param>
	/// <returns>Tuple.</returns>
	private static (bool isValid, int errorCode, string errorMessage) HasValue(string inputString) // tuple return type - (bool isValid, int errorCode, string errorMessage)
	{
		int code = 0;
		string message = string.Empty;

		if (inputString == null)
		{
			code = 1;
			message = "Input string is null";
		}
		else if (inputString.Equals(string.Empty))
		{
			code = 2;
			message = "Input string is empty";
		}
		else if (inputString.Trim().Equals(string.Empty))
		{
			code = 3;
			message = "Input string only whitespaces";
		}

		bool success = string.IsNullOrEmpty(message);

		return (isValid: success, errorCode: code, errorMessage: message); // tuple literal - (isValid: success, errorCode: code, errorMessage: message)
	}
}

Full Working Sample Code to Explain the Use of Deconstruction for User-Defined Types

The OperationResult class with overloaded the Deconstruct methods.

/// <summary>
///     OperationResult.
/// </summary>
public class OperationResult
{
	/// <summary>
	///     Gets or sets a value indicating whether status is true.
	/// </summary>
	public bool Status { get; set; }

	/// <summary>
	///     Gets or sets the error code.
	/// </summary>
	public int ErrorCode { get; set; }

	/// <summary>
	///     Gets or sets the error message.
	/// </summary>
	public string ErrorMessage { get; set; }

	/// <summary>
	///     Gets or sets the Error Message for logging.
	/// </summary>
	public string LogErrorMessage { get; set; }

	/// <summary>
	///     Deconstruct.
	/// </summary>
	/// <param name="status">Status.</param>
	/// <param name="logErrorMessage">Error Message for logging.</param>
	public void Deconstruct(out bool status, out string logErrorMessage)
	{
		status = this.Status;
		logErrorMessage = $"{nameof(this.ErrorCode)}: {this.ErrorCode}, {nameof(this.ErrorMessage)}: {this.ErrorMessage}";
	}

	/// <summary>
	///     Deconstruct.
	/// </summary>
	/// <param name="status">Status.</param>
	/// <param name="errorCode">Error Code.</param>
	/// <param name="errorMessage">Error Message.</param>
	public void Deconstruct(out bool status, out int errorCode, out string errorMessage)
	{
		status = this.Status;
		errorCode = this.ErrorCode;
		errorMessage = this.ErrorMessage;
	}
}

The CSharp7Sample class contains an example I need to explain: deconstructing declaration, deconstructing assignment, and discards in deconstruction.

/// <summary>
///     CSharp7Sample.
/// </summary>
public partial class CSharp7Sample
{
	/// <summary>
	///     Checks Input String is valid (with logging).
	/// </summary>
	/// <param name="inputString">Input string.</param>
	/// <returns>true or false.</returns>
	public static bool IsValidInputStringWithLogging(string inputString)
	{
		(bool isValid, int errorCode, string errorMessage) = CSharp7Sample.HasValue(inputString); // deconstructing declaration - calls Deconstruct(out isValid, out errorCode, out errorMessage);

		if (isValid == false)
		{
			string logMessage = $"{nameof(inputString)}: {inputString}, {nameof(errorCode)}: {errorCode}, {nameof(errorMessage)}: {errorMessage}";
			Console.Write(logMessage);
		}

		return isValid;
	}

	/// <summary>
	///     Checks Input String is valid (with logging).
	/// </summary>
	/// <param name="inputString">Input string.</param>
	/// <param name="errorCode">Error Code.</param>
	/// <param name="errorMessage">Error Message.</param>
	/// <returns>true or false.</returns>
	public static bool IsValidInputStringWithLogging(string inputString, out int errorCode, out string errorMessage)
	{
		bool isValid;
		(isValid, errorCode, errorMessage) = CSharp7Sample.HasValue(inputString); // deconstructing assignment - calls Deconstruct(out isValid, out errorCode, out errorMessage);

		if (isValid == false)
		{
			string logMessage = $"{nameof(inputString)}: {inputString}, {nameof(errorCode)}: {errorCode}, {nameof(errorMessage)}: {errorMessage}";
			Console.Write(logMessage);
		}

		return isValid;
	}

	/// <summary>
	///     Checks Input String is valid (without logging) with discards.
	/// </summary>
	/// <param name="inputString">Input string.</param>
	/// <returns>true or false.</returns>
	public static bool IsValidInputStringWithoutLogging(string inputString)
	{
		(bool isValid, _, _) = CSharp7Sample.HasValue(inputString); // deconstructing declaration with discard. Here, we don't require "errorCode" and "errorMessage"

		return isValid;
	}

	/// <summary>
	///     Checks Input String is valid (with logging) with Deconstructor Overload.
	/// </summary>
	/// <param name="inputString">Input string.</param>
	/// <returns>true or false.</returns>
	public static bool IsValidInputStringWithLoggingWithDeconstructorOverload(string inputString)
	{

		(bool isValid, string logErrorMessage) = CSharp7Sample.HasValue(inputString); // deconstructing declaration - calls Deconstruct(out isValid, out logErrorMessage);

		if (isValid == false)
		{
			string logMessage = $"{nameof(inputString)}: {inputString}, {logErrorMessage}";
			Console.Write(logMessage);
		}

		return isValid;
	}

	/// <summary>
	///     Input string has value or not.
	/// </summary>
	/// <param name="inputString">Input string.</param>
	/// <returns>OperationResult.</returns>
	private static OperationResult HasValue(string inputString)
	{
		int code = 0;
		string message = string.Empty;

		if (inputString == null)
		{
			code = 1;
			message = "Input string is null";
		}
		else if (inputString.Equals(string.Empty))
		{
			code = 2;
			message = "Input string is empty";
		}
		else if (inputString.Trim().Equals(string.Empty))
		{
			code = 3;
			message = "Input string only whitespaces";
		}

		bool success = string.IsNullOrEmpty(message);

		var result = new OperationResult
		{
			ErrorCode = code,
			ErrorMessage = message,
			Status = success 
		};

		return result;
	}
}

Note: Tuple enhancements need the ValueTuple types. Add " System.ValueTuple " NuGet package on frameworks that do not include these types.

Happy Coding !

Deploying code to production can be filled with uncertainty. Reduce the risks, and deploy earlier and more often. Download this free guide to learn more. Brought to you in partnership with Rollbar.

Topics:
web dev ,c# ,c# 7 deconstruction ,c# 7 deconstructor method

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}