Ahmad Masalha
Ahmad Masalha

Reputation: 506

C# Define a tuple as type

Is there a way in C# to define a custom type which holds a tuple? e.g.:

Type MyParams Tuple<string, string> ?

Motive: I have the following function in a parent class:

protected virtual IEnumerable<(string, string)> GetConfigs()

that will be overridden by each child class. I'm looking for a way to define that tuple as type to declare the return type as IEnumerable<Params> instead of duplicating the tuple.

Upvotes: 6

Views: 5217

Answers (3)

Olivier Jacot-Descombes
Olivier Jacot-Descombes

Reputation: 112279

  • Create a record, which is a reference (C# 9+) or value type (C# 10+) which is immutable and behaves like a value type in both cases. A C# record is either a class or a struct having functionality added automatically by the compiler. See also Records (C# reference) and Use record types - C# tutorial | Microsoft Learn.
  • Create a struct, which is a value type.
  • Or declare:
    global using MyParams = System.ValueTuple<string, string>;
    
    Starting with C#12 you can create aliases for any type, i.e., also for ValueTuples:
    global using MyParams = (string key , string value);
    

This global using must precede any other using in the file. Versions prior to C# 12 do not allow creating aliases with the C# tuple syntax (string a, string b). Therefore, you will have to access the tuple elements with t.Item1, t.Item2 etc.

If you omit the global keyword (C# 10+), the scope of the directive is limited to the current file.

Now you can declare the method as

protected virtual IEnumerable<MyParams> GetConfigs()
{
   ...
}

Note; however, that this global using is only known in the current assembly!

Therefore, its use in a public API of a class library is limited.

It is still possible to access members of this library using this type, so.

Having this declaration in one assembly

global using MyParams = System.ValueTuple<string, string>;

namespace MyLibrary;

public class Class1
{
    public MyParams p = ("hello", "world");
}

You can write this in another assembly referencing the first one:

var obj = new MyLibrary.Class1();
Console.WriteLine(obj.p);

But a declaration like MyLibrary.MyParams p; is not possible in the other assembly.

Upvotes: 13

Traveller
Traveller

Reputation: 614

Firstly, I would definitely recommend having a proper defined type given it's for an API (although in fairness I have a dislike for tuples anyway). Something like this might do, with type conversions to/from a (string, string):

public struct SuitableTypeName
{
    public string SomeName;
    public string SomeDifferentName;

    public SuitableTypeName(string someName, string someDifferentName)
    {
        SomeName = someName;
        SomeDifferentName = someDifferentName;
    }

    public static implicit operator (string, string)(SuitableTypeName name)
    {
        return (name.SomeName, name.SomeDifferentName);
    }

    public static explicit operator SuitableTypeName ((string, string) tuple)
    {
        return new SuitableTypeName(tuple.Item1, tuple.Item2);
    }
}

Note that depending you might consider using a class rather than a struct. In this example it's a mutable struct which may lead to confusion/unexpected behavior, so if you're worried about that you could also make it immutable. Also, some groups have very strong feelings about mutable structs for some reason, so if you're working in a group and decide to use it as-is you may want to check your coding standards or clear it with the others.

Upvotes: 4

Konrad Kacperczyk
Konrad Kacperczyk

Reputation: 246

What you're looking for is called alias using directive. However, according to the documentation you can define it with global scope starting from C# 10. Otherwise, you need to make an alias in every file when you want to use it.

using MyParams = System.Tuple<string, string>;

usage:

var foo = new MyParams("foo", "baz");

Upvotes: 6

Related Questions