Tuple sınıflarında “Item1”, “Item2” 'den daha iyi adlandırma


206

Bir Tuple sınıfı kullanmanın ancak içindeki öğelerin adlarını sağlamanın bir yolu var mı?

Örneğin:

public Tuple<int, int, int int> GetOrderRelatedIds()

Bu, OrderGroupId, OrderTypeId, OrderSubTypeId ve OrderRequirementId için kimlikleri döndürür.

Yöntemimin kullanıcılarına hangisinin hangisi olduğunu bildirmek güzel olurdu. (Yöntemi çağırdığınızda sonuçlar sonuçtur.Item1, sonuç.Item2, sonuç.Item3, sonuç.Item4. Hangisinin hangisi olduğu açık değildir.)

(Sadece tüm bu Kimlikleri tutmak için bir sınıf oluşturabileceğimi biliyorum, ama bu Kimlilerin zaten yaşadıkları kendi sınıfları var ve bu yöntemin dönüş değeri için bir sınıf yapmak aptalca görünüyor.)


1
Kendinizi yuvarlamanız gerekecek - Tupleçok genel, bu yüzden elde
edeceğiniz

HAYIR bunu yapamazsınız, daha fazla bilgi için bu bağlantıya bakın msdn.microsoft.com/en-us/vcsharp/ee957397
Enigma Eyaleti

1
Api'niz için halka açık bir veri tipi olarak bir Tuple kullanmanın önerilmeyebileceğini söyleyebilirim. Genellikle Tuple'i kısa ömürlü iç işler için bir API'nin dönüş değeri olarak kullanmıyorum.
Mike Burdick


4
C # 7'nin çalışma listesinde. Bkz. Github.com/dotnet/roslyn/issues/347
Philip Ding

Yanıtlar:


280

C # 7.0'da (Visual Studio 2017) bunu yapmak için yeni bir yapı var:

(string first, string middle, string last) LookupName(long id)

68
Sözdizimi List<(int first, int second)>. Visual Studio 2017'de çalışması için NuGet'ten System.ValueTuple paketini indirmek zorunda kaldım.
Matt Davis

16
Değer yaratmak içinreturn (first: first, middle: middle, last: last);
Fiat

4
veya yalnızca: return (first, middle, last);.NET
4.7.1'de

1
kullanmak için System.ValueTuple nuget paketini eklemeniz gerekecek
Alex G

11
C # 7'nin ValueTuple, genellikle büyük olsa da, değişebilir Tuplebir referans türü ( yapı) iken , değişmez bir referans türü (sınıf) olduğu belirtilmelidir. Bildiğim kadarıyla Tuple, kolay ürün adlarına sahip bir referans türü almanın bir yolu yok .
dx_over_dt

51

C # 7.0'a kadar, kendi türünüzü tanımlamak için bu kadar kısa bir yol yoktu.


14
Bu cevabın 40 puanla kabul edildiğine inanamıyorum. En azından uygun bir kurucuya sahip bir sınıfın bunu nasıl değiştirebileceğini göstermiş olabilirsiniz.
bytecode77

1
@ bytecode77 Pek
MarkPflug

Bu dil festivali önerilerini gördüm. Ancak şimdiye kadar, bir sınıf daha karmaşık veri türleri için tek uygun çözümdür. Neden olursa olsun Tuples'ı zorla kullanmak istersiniz (diğer cevaplara bakın)
bytecode77

3
C # 7 yayınlandıktan sonra, bunu yapmak mümkün olacaktır: msdn.microsoft.com/en-us/magazine/mt595758.aspx
Burak Karakuş

11
Q 'etiket olarak c # 4'e sahiptir, bu nedenle bu cevap kısa olmasına rağmen hala doğrudur.
Steve Drake

33

İşte sorduğunuz şeyin aşırı karmaşık bir versiyonu:

class MyTuple : Tuple<int, int>
{
    public MyTuple(int one, int two)
        :base(one, two)
    {

    }

    public int OrderGroupId { get{ return this.Item1; } }
    public int OrderTypeId { get{ return this.Item2; } }

}

Neden sadece bir sınıf yapmıyorsun?


2
Class yerine bu durumda yapı daha iyi olur mu?
deathrace

5
Bunun gördüğüm küçük avantajı, eşittir operatörünü otomatik olarak uygulaması ve öğelerin eşit olması durumunda 2 örneğin eşit olup olmadığını kontrol etmesidir.
JSoet

8
Bu yaklaşımın bir diğer dezavantajı, Item1 ve Item2'nin MyTuple
RJFalconer'da

3
@deathrace Tuple'in kendisi sınıflardır, bu yüzden doğrudan miras almak Tuple<T, T2>istiyorsanız bir yapı olamazsınız.
Chakrava

3
Yanılıyor olabilirim ama çoğunlukla bir nesne döndürmek istediğim yerde tuple kullanıyorum ama belirli bir sınıf tanımlamak istemiyorum ..
Jay

12

.Net 4 ile belki de ExpandoObjectbu basit durum için derleme zamanı hatalarının çalışma zamanı hataları olacağı için kullanmayın.

class Program
{
    static void Main(string[] args)
    {
        dynamic employee, manager;

        employee = new ExpandoObject();
        employee.Name = "John Smith";
        employee.Age = 33;

        manager = new ExpandoObject();
        manager.Name = "Allison Brown";
        manager.Age = 42;
        manager.TeamSize = 10;

        WritePerson(manager);
        WritePerson(employee);
    }
    private static void WritePerson(dynamic person)
    {
        Console.WriteLine("{0} is {1} years old.",
                          person.Name, person.Age);
        // The following statement causes an exception
        // if you pass the employee object.
        // Console.WriteLine("Manages {0} people", person.TeamSize);
    }
}
// This code example produces the following output:
// John Smith is 33 years old.
// Allison Brown is 42 years old.

Bahsetmeye değer başka bir şey, bir yöntem içinde anonim bir türdür , ancak döndürmek istiyorsanız bir sınıf oluşturmanız gerekir.

var MyStuff = new
    {
        PropertyName1 = 10,
        PropertyName2 = "string data",
        PropertyName3 = new ComplexType()
    };

10

Benim cevap çoğaltılması bu burada daha iyi bir seçimdir olarak yazı.

C # V7.0 başlayarak, şimdi daha erken gibi önceden tanımlanmış isimlere varsayılan için kullanılan tanımlama grubu özelliklerini isimlendirmek mümkündür Item1, Item2vb.

Tuple Değişmezlerinin özelliklerini adlandırma :

var myDetails = (MyName: "RBT_Yoga", MyAge: 22, MyFavoriteFood: "Dosa");
Console.WriteLine($"Name - {myDetails.MyName}, Age - {myDetails.MyAge}, Passion - {myDetails.MyFavoriteFood}");

Konsoldaki çıktı:

İsim - RBT_Yoga, Yaş - 22, Tutku - Dosa

Yöntemden adlandırılmış Tuple (adlandırılmış özelliklere sahip) :

static void Main(string[] args)
{
    var empInfo = GetEmpInfo();
    Console.WriteLine($"Employee Details: {empInfo.firstName}, {empInfo.lastName}, {empInfo.computerName}, {empInfo.Salary}");
}

static (string firstName, string lastName, string computerName, int Salary) GetEmpInfo()
{
    //This is hardcoded just for the demonstration. Ideally this data might be coming from some DB or web service call
    return ("Rasik", "Bihari", "Rasik-PC", 1000);
}

Konsoldaki çıktı:

Çalışan Detayları: Rasik, Bihari, Rasik-PC, 1000

Adlandırılmış özelliklere sahip Tuples listesi oluşturma

var tupleList = new List<(int Index, string Name)>
{
    (1, "cow"),
    (5, "chickens"),
    (1, "airplane")
};

foreach (var tuple in tupleList)
    Console.WriteLine($"{tuple.Index} - {tuple.Name}");

Konsol çıkışı:

1 - inek 5 - tavuk 1 - uçak

Umarım herşeyi ele alırım. Durumda, kaçırdığım bir şey varsa lütfen yorumlarda bana bir geri bildirim verin.

Not : Kod parçacıklarım, burada ayrıntılı olarak açıklandığı gibi C # v7'nin dize enterpolasyon özelliğini kullanıyor .


3

MichaelMocko Cevaplandı harika,

ama çözmem gereken birkaç şey eklemek istiyorum

(string first, string middle, string last) LookupName(long id)

.net framework <4.7 kullanıyorsanız yukarıdaki satır derleme zamanı hatası verecektir.

Yani .net framework <4.7 kullanan bir projeniz varsa ve yine de ValueTuple kullanmak istiyorsanız workAround bu nuget paketini kuruyor olacaktır



2

Eşyalarınızın türleri farklıysa, bunları daha sezgisel olarak elde etmek için yaptığım bir sınıf.

Bu sınıfın kullanımı:

var t = TypedTuple.Create("hello", 1, new MyClass());
var s = t.Get<string>();
var i = t.Get<int>();
var c = t.Get<MyClass>();

Kaynak kodu:

public static class TypedTuple
{
    public static TypedTuple<T1> Create<T1>(T1 t1)
    {
        return new TypedTuple<T1>(t1);
    }

    public static TypedTuple<T1, T2> Create<T1, T2>(T1 t1, T2 t2)
    {
        return new TypedTuple<T1, T2>(t1, t2);
    }

    public static TypedTuple<T1, T2, T3> Create<T1, T2, T3>(T1 t1, T2 t2, T3 t3)
    {
        return new TypedTuple<T1, T2, T3>(t1, t2, t3);
    }

    public static TypedTuple<T1, T2, T3, T4> Create<T1, T2, T3, T4>(T1 t1, T2 t2, T3 t3, T4 t4)
    {
        return new TypedTuple<T1, T2, T3, T4>(t1, t2, t3, t4);
    }

    public static TypedTuple<T1, T2, T3, T4, T5> Create<T1, T2, T3, T4, T5>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
    {
        return new TypedTuple<T1, T2, T3, T4, T5>(t1, t2, t3, t4, t5);
    }

    public static TypedTuple<T1, T2, T3, T4, T5, T6> Create<T1, T2, T3, T4, T5, T6>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
    {
        return new TypedTuple<T1, T2, T3, T4, T5, T6>(t1, t2, t3, t4, t5, t6);
    }

    public static TypedTuple<T1, T2, T3, T4, T5, T6, T7> Create<T1, T2, T3, T4, T5, T6, T7>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
    {
        return new TypedTuple<T1, T2, T3, T4, T5, T6, T7>(t1, t2, t3, t4, t5, t6, t7);
    }

    public static TypedTuple<T1, T2, T3, T4, T5, T6, T7, T8> Create<T1, T2, T3, T4, T5, T6, T7, T8>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
    {
        return new TypedTuple<T1, T2, T3, T4, T5, T6, T7, T8>(t1, t2, t3, t4, t5, t6, t7, t8);
    }

}

public class TypedTuple<T>
{
    protected Dictionary<Type, object> items = new Dictionary<Type, object>();

    public TypedTuple(T item1)
    {
        Item1 = item1;
    }

    public TSource Get<TSource>()
    {
        object value;
        if (this.items.TryGetValue(typeof(TSource), out value))
        {
            return (TSource)value;
        }
        else
            return default(TSource);
    }

    private T item1;
    public T Item1 { get { return this.item1; } set { this.item1 = value; this.items[typeof(T)] = value; } }
}

public class TypedTuple<T1, T2> : TypedTuple<T1>
{
    public TypedTuple(T1 item1, T2 item2)
        : base(item1)
    {
        Item2 = item2;
    }

    private T2 item2;
    public T2 Item2 { get { return this.item2; } set { this.item2 = value; this.items[typeof(T2)] = value; } }
}

public class TypedTuple<T1, T2, T3> : TypedTuple<T1, T2>
{
    public TypedTuple(T1 item1, T2 item2, T3 item3)
        : base(item1, item2)
    {
        Item3 = item3;
    }

    private T3 item3;
    public T3 Item3 { get { return this.item3; } set { this.item3 = value; this.items[typeof(T3)] = value; } }
}

public class TypedTuple<T1, T2, T3, T4> : TypedTuple<T1, T2, T3>
{
    public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4)
        : base(item1, item2, item3)
    {
        Item4 = item4;
    }

    private T4 item4;
    public T4 Item4 { get { return this.item4; } set { this.item4 = value; this.items[typeof(T4)] = value; } }
}

public class TypedTuple<T1, T2, T3, T4, T5> : TypedTuple<T1, T2, T3, T4>
{
    public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5)
        : base(item1, item2, item3, item4)
    {
        Item5 = item5;
    }

    private T5 item5;
    public T5 Item5 { get { return this.item5; } set { this.item5 = value; this.items[typeof(T5)] = value; } }
}

public class TypedTuple<T1, T2, T3, T4, T5, T6> : TypedTuple<T1, T2, T3, T4, T5>
{
    public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6)
        : base(item1, item2, item3, item4, item5)
    {
        Item6 = item6;
    }

    private T6 item6;
    public T6 Item6 { get { return this.item6; } set { this.item6 = value; this.items[typeof(T6)] = value; } }
}

public class TypedTuple<T1, T2, T3, T4, T5, T6, T7> : TypedTuple<T1, T2, T3, T4, T5, T6>
{
    public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7)
        : base(item1, item2, item3, item4, item5, item6)
    {
        Item7 = item7;
    }

    private T7 item7;
    public T7 Item7 { get { return this.item7; } set { this.item7 = value; this.items[typeof(T7)] = value; } }
}

public class TypedTuple<T1, T2, T3, T4, T5, T6, T7, T8> : TypedTuple<T1, T2, T3, T4, T5, T6, T7>
{
    public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8)
        : base(item1, item2, item3, item4, item5, item6, item7)
    {
        Item8 = item8;
    }

    private T8 item8;
    public T8 Item8 { get { return this.item8; } set { this.item8 = value; this.items[typeof(T8)] = value; } }
}

4
Bu, çok az veya hiç kazanç için çok fazla iş gibi görünüyor. Sezgisel bir sınırlaması vardır (yinelenen türler yoktur) ve bir değeri sadece türüne göre inanılmaz derecede sezgisel olarak alma fikrini buluyorum ve bunun için pratik bir kullanım durumu düşünemiyorum. Bu, çalışanlar için bir veri tablosu oluşturmaya, ardından çalışanları ilk adlarıyla (benzersiz bir anahtarın aksine) almaya karar vermeye ve daha sonra tüm çalışanların farklı adlara sahip olmasını gerektirmeye eşdeğerdir. Bu bir soruna çözüm değil, ekstra bir sorun yaratma pahasına bir çözüm kullanıyor.
flater

Ve Tanrı ruhuna rahmet etsin.
Jamie

1

Bu çok can sıkıcı ve C # gelecekteki sürümleri bu ihtiyacı giderecek bekliyoruz. Farklı bir veri yapısı türü kullanmak veya akıl sağlığınız ve kodunuzu okuyan başkalarının akıl sağlığı için "öğeleri" yeniden adlandırmak için en kolay iş buluyorum.

Tuple<ApiResource, JSendResponseStatus> result = await SendApiRequest();
ApiResource apiResource = result.Item1;
JSendResponseStatus jSendStatus = result.Item2;

0

Ben bir sınıf oluşturmak düşünüyorum ama başka bir alternatif çıktı parametreleri.

public void GetOrderRelatedIds(out int OrderGroupId, out int OrderTypeId, out int OrderSubTypeId, out int OrderRequirementId)

Tuple'ınız yalnızca tamsayılar içerdiğinden, Dictionary<string,int>

var orderIds = new Dictionary<string, int> {
    {"OrderGroupId", 1},
    {"OrderTypeId", 2},
    {"OrderSubTypeId", 3},
    {"OrderRequirementId", 4}.
};

ama ben de tavsiye etmiyorum.


0

Neden herkes hayatı bu kadar zorlaştırıyor? Tuples oldukça geçici veri işleme içindir. Tuples ile her zaman çalışmak, kodun bir noktada anlaşılmasını çok zorlaştıracaktır. Her şey için sınıflar oluşturmak sonunda projenizi şişirebilir.

Ancak bu denge ile ilgili ...

Sorununuz bir sınıf isteyeceğiniz bir şey gibi görünüyor. Ve sadece bütünlük adına, aşağıdaki bu sınıf aynı zamanda yapıcıları da içerir.


Bu,

  • Özel bir veri türü
    • daha fazla işlevsellik olmadan. Harfler ve ayarlayıcılar da kodla genişletilebilir, özel üyeler "_orderGroupId" ad desenine sahip olur / ayarlanırken işlevsel kod yürütülür.
  • İnşaatçılar dahil. Ayrıca, tüm özellikler zorunluysa yalnızca bir kurucu eklemeyi de seçebilirsiniz .
  • Tüm kurucuları kullanmak istiyorsanız, yinelenen kodlardan kaçınmak için bu şekilde köpürmek uygun kalıptır.

public class OrderRelatedIds
{
    public int OrderGroupId { get; set; }
    public int OrderTypeId { get; set; }
    public int OrderSubTypeId { get; set; }
    public int OrderRequirementId { get; set; }

    public OrderRelatedIds()
    {
    }
    public OrderRelatedIds(int orderGroupId)
        : this()
    {
        OrderGroupId = orderGroupId;
    }
    public OrderRelatedIds(int orderGroupId, int orderTypeId)
        : this(orderGroupId)
    {
        OrderTypeId = orderTypeId;
    }
    public OrderRelatedIds(int orderGroupId, int orderTypeId, int orderSubTypeId)
        : this(orderGroupId, orderTypeId)
    {
        OrderSubTypeId = orderSubTypeId;
    }
    public OrderRelatedIds(int orderGroupId, int orderTypeId, int orderSubTypeId, int orderRequirementId)
        : this(orderGroupId, orderTypeId, orderSubTypeId)
    {
        OrderRequirementId = orderRequirementId;
    }
}

Veya gerçekten basit olmasını istiyorsanız: Tür başlatıcıları da kullanabilirsiniz:

OrderRelatedIds orders = new OrderRelatedIds
{
    OrderGroupId = 1,
    OrderTypeId = 2,
    OrderSubTypeId = 3,
    OrderRequirementId = 4
};

public class OrderRelatedIds
{
    public int OrderGroupId;
    public int OrderTypeId;
    public int OrderSubTypeId;
    public int OrderRequirementId;
}

0

Summay'da Item isimlerini yazarım .. helloworld () fonksiyonunun üzerine gelindiğinde metin hello = Item1 ve world = Item2 diyecektir

 helloworld("Hi1,Hi2");

/// <summary>
/// Return hello = Item1 and world Item2
/// </summary>
/// <param name="input">string to split</param>
/// <returns></returns>
private static Tuple<bool, bool> helloworld(string input)
{
    bool hello = false;
    bool world = false;
    foreach (var hw in input.Split(','))
    {
        switch (hw)
        {
            case "Hi1":
                hello= true;
                break;
            case "Hi2":
                world= true;
                break;
        }

    }
    return new Tuple<bool, bool>(hello, world);
}

0

Sadece @MichaelMocko cevabına eklemek için. Tuples şu anda birkaç gotcha var:

Onları EF ifade ağaçlarında kullanamazsınız

Misal:

public static (string name, string surname) GetPersonName(this PersonContext ctx, int id)
{
    return ctx.Persons
        .Where(person => person.Id == id)
        // Selecting as Tuple
        .Select(person => (person.Name, person.Surname))
        .First();
}

Bu, "Bir ifade ağacı bir tam çift içermeyebilir" hatasıyla derlenemez. Ne yazık ki, bunlar dile eklendiğinde ifade ağaçları API'sı tuples desteği ile genişletilmedi.

Güncellemeler için bu sorunu takip edin (ve oylayın): https://github.com/dotnet/roslyn/issues/12897

Sorunu çözmek için, önce anonim türe yazabilir ve sonra değeri tuple'e dönüştürebilirsiniz:

// Will work
public static (string name, string surname) GetPersonName(this PersonContext ctx, int id)
{
    return ctx.Persons
        .Where(person => person.Id == id)
        .Select(person => new { person.Name, person.Surname })
        .ToList()
        .Select(person => (person.Name, person.Surname))
        .First();
}

Başka bir seçenek ValueTuple.Create kullanmaktır:

// Will work
public static (string name, string surname) GetPersonName(this PersonContext ctx, int id)
{
    return ctx.Persons
        .Where(person => person.Id == id)
        .Select(person => ValueTuple.Create(person.Name, person.Surname))
        .First();
}

Referanslar:

Onları lambdalarda yapılandıramazsın

Desteği eklemek için bir teklif var: https://github.com/dotnet/csharplang/issues/258

Misal:

public static IQueryable<(string name, string surname)> GetPersonName(this PersonContext ctx, int id)
{
    return ctx.Persons
        .Where(person => person.Id == id)
        .Select(person => ValueTuple.Create(person.Name, person.Surname));
}

// This won't work
ctx.GetPersonName(id).Select((name, surname) => { return name + surname; })

// But this will
ctx.GetPersonName(id).Select(t => { return t.name + t.surname; })

Referanslar:

Güzelce serileştirilmeyecekler

using System;
using Newtonsoft.Json;

public class Program
{
    public static void Main() {
        var me = (age: 21, favoriteFood: "Custard");
        string json = JsonConvert.SerializeObject(me);

        // Will output {"Item1":21,"Item2":"Custard"}
        Console.WriteLine(json); 
    }
}

Grup alanı adları yalnızca derleme zamanında kullanılabilir ve çalışma zamanında tamamen silinir.

Referanslar:


-1

Tuple içeren bir sınıf yazabilirsiniz.

Eşittir ve GetHashCode işlevlerini geçersiz kılmanız gerekir

ve == ve! = operatörleri.

class Program
{
    public class MyTuple
    {
        private Tuple<int, int> t;

        public MyTuple(int a, int b)
        {
            t = new Tuple<int, int>(a, b);
        }

        public int A
        {
            get
            {
                return t.Item1;
            }
        }

        public int B
        {
            get
            {
                return t.Item2;
            }
        }

        public override bool Equals(object obj)
        {
            return t.Equals(((MyTuple)obj).t);
        }

        public override int GetHashCode()
        {
            return t.GetHashCode();
        }

        public static bool operator ==(MyTuple m1, MyTuple m2)
        {
            return m1.Equals(m2);
        }

        public static bool operator !=(MyTuple m1, MyTuple m2)
        {
            return !m1.Equals(m2);
        }
    }

    static void Main(string[] args)
    {
        var v1 = new MyTuple(1, 2);
        var v2 = new MyTuple(1, 2);

        Console.WriteLine(v1 == v2);

        Dictionary<MyTuple, int> d = new Dictionary<MyTuple, int>();
        d.Add(v1, 1);

        Console.WriteLine(d.ContainsKey(v2));
    }
}

dönecek:

Doğru

Doğru


2
Bu veri türü için zaten bir sınıf uyguladıysanız, neden yalnızca özellikler yerine temel veriler için bir Tuple bildiriyorsunuz?
bytecode77

Eşittir işlevinde
ss

Bu bir bonus olabilir. Ancak diğer taraftan, temel olarak Item1 ile ItemX arasında değişen özelliklere sahip bir sınıf yarattınız. Bir tuple kullanarak eşit adlandırmak ve Equals () daha fazla kod seçiyor.
bytecode77

-1

C # 7 demet örneği

var tuple = TupleExample(key, value);

     private (string key1, long value1) ValidateAPIKeyOwnerId(string key, string value)
            {
                return (key, value);
            }
      if (!string.IsNullOrEmpty(tuple.key1) && tuple.value1 > 0)
          {
                    //your code

                }     
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.