Bir yöntem arayanına birden çok değer döndürme


439

Bu sorunun C ++ sürümünü okudum ama gerçekten anlamadım.

Birisi bunun nasıl yapılacağını ve nasıl yapılacağını açık bir şekilde açıklayabilir mi?

Yanıtlar:


609

C # 7 ve üstü için bu cevaba bakınız .

Önceki sürümlerde, .NET 4.0 + 's Tuple'ı kullanabilirsiniz :

Örneğin:

public Tuple<int, int> GetMultipleValue()
{
     return Tuple.Create(1,2);
}

İki değer var olan Tuplelar Item1ve Item2özellikler olarak.


8
Öğe1, Öğe2 ve bunun yerine adlandırılmış çıktı değerlerini kullanmak çok güzel olurdu. C # 7 muhtemelen bunu sağlayacaktır .
Sнаđошƒаӽ

1
@ Sнаđошƒаӽ kesinlikle doğrudur, bunun yaklaşan C # 7.0'da aşağıdaki gibi bir sözdizimi kullanılarak desteklenmesi beklenmektedir: public (int sum, int count) GetMultipleValues() { return (1, 2); }Bu örnek, bununla ilgili Belgeler konu örneğimizden alınmıştır .
Jeppe Stig Nielsen

435

Şimdi C # 7 yayınlandığına göre, yeni eklenen Tuples sözdizimini kullanabilirsiniz

(string, string, string) LookupName(long id) // tuple return type
{
    ... // retrieve first, middle and last from data storage
    return (first, middle, last); // tuple literal
}

daha sonra şu şekilde kullanılabilir:

var names = LookupName(id);
WriteLine($"found {names.Item1} {names.Item3}.");

Öğelerinize ad da verebilirsiniz (bu nedenle "Öğe1", "Öğe2" vb. Değildir). İmzaya veya iade yöntemlerine bir ad ekleyerek bunu yapabilirsiniz:

(string first, string middle, string last) LookupName(long id) // tuple elements have names

veya

return (first: first, middle: middle, last: last); // named tuple elements in a literal

Ayrıca yapısız olabilirler, bu oldukça güzel bir yeni özelliktir:

(string first, string middle, string last) = LookupName(id1); // deconstructing declaration

Neler yapılabileceğine ilişkin daha fazla örnek görmek için bu bağlantıya göz atın :)


11
.NET Framework 4.7 veya .NET Core 2.0'dan önceki bir şeyi hedefliyorsanız, bir NuGet paketi yüklemeniz gerekir .
Phil

1
Dönüş elde etmek için şunları yapabilirsiniz: "var results = LookupName (5); Console.WriteLine (sonuç.middle)".
alansiqueira27

204

Üç farklı yol kullanabilirsiniz

1. ref / out parametreleri

ref kullanarak:

static void Main(string[] args)
{
    int a = 10;
    int b = 20;
    int add = 0;
    int multiply = 0;
    Add_Multiply(a, b, ref add, ref multiply);
    Console.WriteLine(add);
    Console.WriteLine(multiply);
}

private static void Add_Multiply(int a, int b, ref int add, ref int multiply)
{
    add = a + b;
    multiply = a * b;
}

kullanarak:

static void Main(string[] args)
{
    int a = 10;
    int b = 20;
    int add;
    int multiply;
    Add_Multiply(a, b, out add, out multiply);
    Console.WriteLine(add);
    Console.WriteLine(multiply);
}

private static void Add_Multiply(int a, int b, out int add, out int multiply)
{
    add = a + b;
    multiply = a * b;
}

2. yapı / sınıf

struct kullanarak:

struct Result
{
    public int add;
    public int multiply;
}
static void Main(string[] args)
{
    int a = 10;
    int b = 20;
    var result = Add_Multiply(a, b);
    Console.WriteLine(result.add);
    Console.WriteLine(result.multiply);
}

private static Result Add_Multiply(int a, int b)
{
    var result = new Result
    {
        add = a * b,
        multiply = a + b
    };
    return result;
}

sınıf kullanma:

class Result
{
    public int add;
    public int multiply;
}
static void Main(string[] args)
{
    int a = 10;
    int b = 20;
    var result = Add_Multiply(a, b);
    Console.WriteLine(result.add);
    Console.WriteLine(result.multiply);
}

private static Result Add_Multiply(int a, int b)
{
    var result = new Result
    {
        add = a * b,
        multiply = a + b
    };
    return result;
}

3. Demet

Tuple sınıfı

static void Main(string[] args)
{
    int a = 10;
    int b = 20;
    var result = Add_Multiply(a, b);
    Console.WriteLine(result.Item1);
    Console.WriteLine(result.Item2);
}

private static Tuple<int, int> Add_Multiply(int a, int b)
{
    var tuple = new Tuple<int, int>(a + b, a * b);
    return tuple;
}

C # 7 Tuples

static void Main(string[] args)
{
    int a = 10;
    int b = 20;
    (int a_plus_b, int a_mult_b) = Add_Multiply(a, b);
    Console.WriteLine(a_plus_b);
    Console.WriteLine(a_mult_b);
}

private static (int a_plus_b, int a_mult_b) Add_Multiply(int a, int b)
{
    return(a + b, a * b);
}

3
Sadece kendi sadakatimle, hangisinin en hızlı ve 'en iyi uygulama' olduğunu söyleyebilirsin?
Netferret

'using struct' için en iyi örnek :)
SHEKHAR SHETE

1
c # 7 sözdizimi eklemek için düşündüren (ve daha fazla kişi bu kadar :) oy)
twomm

Bilginiz için küçük (alakasız) bir yazım hatası: Yapı / sınıf çözümlerinde toplama / çarpma işlemlerini karıştırdınız.
Szak1

C # Tuple sözdiziminin bir şey olduğunu bilmiyordum! Yıllar sonra bile yeni bir şeyler öğrenin!
jhaagsma

75

Bunu C # ile yapamazsınız. Yapabileceğiniz şey, bir outparametreye sahip olmak veya kendi sınıfınızı döndürmektir (veya değişmez olmasını istiyorsanız, yapı).

Out parametresini kullanma
public int GetDay(DateTime date, out string name)
{
  // ...
}
Özel sınıf (veya yapı) kullanma
public DayOfWeek GetDay(DateTime date)
{
  // ...
}

public class DayOfWeek
{
  public int Day { get; set; }
  public string Name { get; set; }
}

24
Bu durumda bir alternatif, dönüş türü için sınıf yerine bir yapı kullanmaktır. Dönüş değeri vatansız ve geçici ise, yapı daha iyi bir seçimdir.
Michael Meadows

1
Bu asyncyöntemlerle mümkün değildir . Tuplegitmek için bir yoldur. ( outEşzamanlı işlemlerde parametreleri kullanıyorum ; bu durumlarda gerçekten yararlılar.)
Codefun64

5
Bu şimdi C # 7'de mümkündür: (int, int) Method () {return (1, 2); }
Spook

4
Cevabın güncellenmesi gerekiyor, c # 'ın son sürümleriyle yanlış anlaşıldı. güncellenirse downvote upvote olarak değiştirilir.
whitneyland

Eski bir kod tabanı üzerinde çalışmak, özel bir sınıf döndürmek benim için sağlam bir yaklaşımdı.
Brant

38

Birden çok değer döndürmek istiyorsanız, döndürmek istediğiniz değerleri içeren bir sınıf / yapı döndürebilir veya parametrelerinizdeki "out" anahtar kelimesini şu şekilde kullanabilirsiniz:

public void Foo(int input, out int output1, out string output2, out string errors) {
    // set out parameters inside function
}

2
"Out" veya "ref" kullanmanın iyi olduğunu düşünmüyorum - çünkü tamamen kendi sınıf türünüzden dönen bir değerle değiştirilebilir. "ref" kullanıyorsanız, bu tür parametrelere nasıl atanacağınızı görüyorsunuz? (Sadece içeride kodlamaya bağlıdır). Eğer işlevin gövdesinde, yazar parametreye "ref" ile bir örnek "yenisini" eklediyse, oraya "nullable" değerini iletebileceğiniz anlamına gelir. Diğer değil. Yani bu biraz belirsiz. Ve biz daha iyi yollar var (1. kendi sınıf Dönen, 2. Turple).

33

Önceki poster doğru. Bir C # yönteminden birden çok değer döndüremezsiniz. Ancak, birkaç seçeneğiniz var:

  • Birden çok üye içeren bir yapı döndürme
  • Sınıf örneğini döndürme
  • Çıktı parametrelerini kullanın ( out veya ref anahtar kelimelerini kullanarak )
  • Çıktı olarak sözlük veya anahtar / değer çifti kullanma

Buradaki artıları ve eksileri anlamak genellikle zordur. Bir yapı döndürürseniz, yapıların değer türü olduğu ve yığına iletildiği için küçük olduğundan emin olun. Bir sınıf örneğini döndürürseniz, sorunlara neden olmamak için burada kullanmak isteyebileceğiniz bazı tasarım desenleri vardır - C # nesneleri referans ile geçtiğinden sınıf üyeleri değiştirilebilir (VB'de yaptığınız gibi ByVal'a sahip değilsiniz) ).

Son olarak çıkış parametrelerini kullanabilirsiniz, ancak parametrelerin yalnızca birkaçı (3 veya daha az gibi) olduğunda senaryolarla sınırlı kalırım - aksi takdirde işler çirkin ve bakımı zorlaşır. Ayrıca, çıktı parametrelerinin kullanımı çeviklik için bir engelleyici olabilir, çünkü yöntem imzanız, dönüş değerine her şey eklemeniz gerektiğinde değişmek zorunda kalacakken, bir yapı veya sınıf örneğini döndürmek için yöntem imzasını değiştirmeden üye ekleyebilirsiniz.

Mimari açıdan, anahtar / değer çiftlerini veya sözlükleri kullanmamanızı tavsiye ederim. Ben bu kodlama tarzı yöntemi tüketen kod "gizli bilgi" gerektirir bulmak. Anahtarların ne olacağını ve değerlerin ne anlama geldiğini önceden bilmelidir ve dahili uygulamada çalışan geliştirici, sözlük veya KVP'nin oluşturulma şeklini değiştirirse, tüm uygulama boyunca kolayca bir hata kademesi oluşturabilir.


Ayrıca Exception, döndürmek istediğiniz ikinci değerin ilk değerden kopması durumunda bir de atayabilirsiniz: bir tür başarılı değer veya bir tür başarısız değer döndürmek istediğinizde olduğu gibi.
Coeur

21

Ya bir dönüş sınıf örneğini veya kullanım dışarı parametrelerini. Out parametrelerine bir örnek:

void mymethod(out int param1, out int param2)
{
    param1 = 10;
    param2 = 20;
}

Buna şöyle deyin:

int i, j;
mymethod(out i, out j);
// i will be 20 and j will be 10

3
Unutmayın, bu sadece yapabileceğiniz için bunu yapmanız gerektiği anlamına gelmez. Bu, .Net'te çoğu durumda yaygın olarak kötü bir uygulama olarak kabul edilmektedir.
Michael Meadows

4
Bunun neden kötü bir uygulama olduğunu açıklayabilir misiniz?
Zo

C / C ++ 'da kötü bir uygulama. Sorun "yan etki ile programlama": int GetLength (char * s) {int n = 0; (s [n]! = '\ 0') n ++; s [1] = 'X'; dönüş (n); int main () {karakter tebrik [5] = {'H', 'e', ​​'l', 'p', '\ 0'}; int len ​​= GetLength (tebrik); cout << len << ":" << karşılama; // Çıktı: 5: HXlp} C # 'da şunu yazmanız gerekir: int len ​​= GetLength (ref selamlama) "Hey, selamlama bunu çağırdıktan sonra aynı olmayacak" ve büyük bir uyarı işareti hataları azaltmak.
Dustin_00

19

Birçok yol var; ancak yeni bir Nesne veya yapı veya bunun gibi bir şey oluşturmak istemiyorsanız, C # 7.0'dan sonra aşağıdaki gibi yapabilirsiniz :

 (string firstName, string lastName) GetName(string myParameter)
    {
        var firstName = myParameter;
        var lastName = myParameter + " something";
        return (firstName, lastName);
    }

    void DoSomethingWithNames()
    {
        var (firstName, lastName) = GetName("myname");

    }

13

C # 7'de yeni bir Tuplesözdizimi var:

static (string foo, int bar) GetTuple()
{
    return ("hello", 5);
}

Bunu bir kayıt olarak döndürebilirsiniz:

var result = GetTuple();
var foo = result.foo
// foo == "hello"

Yeni yapısökücü sözdizimini de kullanabilirsiniz:

(string foo) = GetTuple();
// foo == "hello"

Ancak serileştirme dikkatli olun, bütün bu sözdizimsel şeker - gerçek derlenmiş kod bu olacak Tuple<string, int>(aynı kabul Yanıt başına birlikte) Item1ve Item2yerine foovebar . Bu, serileştirmenin (veya serileştirmenin) bunun yerine bu özellik adlarını kullanacağı anlamına gelir.

Yani, serileştirme için bir kayıt sınıfı bildirin ve bunun yerine geri gönderin.

Ayrıca C # 7'de yeni olan, outparametreler için geliştirilmiş bir sözdizimidir . Artık outbazı bağlamlarda daha uygun olan satır içi bildirebilirsiniz :

if(int.TryParse("123", out int result)) {
    // Do something with result
}

Ancak, bunu çoğunlukla kendi işlevleriniz yerine .NET'in kendi kitaplıklarında kullanırsınız.


Hedeflediğiniz .Net sürümüne bağlı olarak, Nuget paket System.ValueTuple'i yüklemeniz gerekebileceğini lütfen unutmayın.
Licht

Yukarıdaki gibi cevap vermek
üzereydim

12

Bazı cevaplar parametrelerin kullanılmasını önermez, ancak zaman uyumsuz yöntemlerle çalışmadığı için bunu kullanmamanızı öneririm . Bkz bu fazla bilgi için.

Diğer cevaplar da ben tavsiye ederim ama C # 7.0 ile sunulan yeni özelliği kullanarak Tuple kullanarak belirtti.

(string, string, string) LookupName(long id) // tuple return type
{
    ... // retrieve first, middle and last from data storage
    return (first, middle, last); // tuple literal
}

var names = LookupName(id);
WriteLine($"found {names.Item1} {names.Item3}.");

Daha fazla bilgi burada bulunabilir .


11

Bunu yapmanın birkaç yolu vardır. refParametreleri kullanabilirsiniz :

int Foo(ref Bar bar) { }

Bu, işleve bir başvuru ileterek işlevin çağrı kodunun yığınındaki nesneyi değiştirmesine izin verir. Bu teknik olarak "döndürülen" bir değer olmamakla birlikte, bir işleve benzer bir şey yapmanın bir yoludur. Yukarıdaki kodda, işlev bir intve (potansiyel olarak) bir değişiklik döndürür bar.

Bir diğer benzer yaklaşım bir outparametre kullanmaktır . Bir outparametre özdeş refek bir derleyici zorlanan kurala parametresi. Bu kural, bir outparametreye bir işleve iletirseniz, geri dönmeden önce değerini ayarlamak için bu işlevin gerekli olmasıdır. Bu kuralın yanı sıra, bir outparametre tıpkı bir refparametre gibi çalışır .

Son yaklaşım (ve çoğu durumda en iyisi) her iki değeri de içine alan ve işlevin bunu döndürmesine izin veren bir tür oluşturmaktır:

class FooBar 
{
    public int i { get; set; }
    public Bar b { get; set; }
}

FooBar Foo(Bar bar) { }

Bu son yaklaşım daha basit ve okunması ve anlaşılması daha kolaydır.


11

Hayır, C # 'daki bir işlevden (C # 7'den düşük sürümler için) birden çok değer döndüremezsiniz, en azından Python'da yapabileceğiniz şekilde değil.

Ancak, birkaç alternatif var:

İstediğiniz birden çok değeri içeren bir yazı tipi dizisi döndürebilirsiniz.

private object[] DoSomething()
{
    return new [] { 'value1', 'value2', 3 };
}

outParametreleri kullanabilirsiniz .

private string DoSomething(out string outparam1, out int outparam2)
{
    outparam1 = 'value2';
    outparam2 = 3;
    return 'value1';
}

10

C # 4'te, bunu kolayca halletmek için tuples için yerleşik desteği kullanabilirsiniz.

Bu arada iki seçenek var.

İlk olarak, parametrelerinize arama rutinine geri gönderilen değerleri atamak için ref veya out parametrelerini kullanabilirsiniz.

Bu şuna benzer:

void myFunction(ref int setMe, out int youMustSetMe);

İkincisi, dönüş değerlerinizi bir yapıya veya sınıfa sarıp bunları o yapının üyeleri olarak iletebilirsiniz. KeyValuePair 2 için iyi çalışır - 2'den fazla için özel bir sınıfa veya yapıya ihtiyacınız olacaktır.


7

bunu "KeyValuePair" deneyebilirsiniz

private KeyValuePair<int, int> GetNumbers()
{
  return new KeyValuePair<int, int>(1, 2);
}


var numbers = GetNumbers();

Console.WriteLine("Output : {0}, {1}",numbers.Key, numbers.Value);

Çıktı :

Çıkış: 1, 2


5

Sınıflar, Yapılar, Koleksiyonlar ve Diziler birden çok değer içerebilir. Çıkış ve referans parametreleri de bir fonksiyonda ayarlanabilir. Birden fazla değer döndürmek, tuples aracılığıyla dinamik ve fonksiyonel dillerde mümkündür, ancak C # 'de mümkün değildir.


4

Temelde iki yöntem vardır. 1. out / ref parametrelerini kullanma 2. Nesneler dizisi döndürme


Ayrıca tuples ve tuples için sözdizimsel şeker olarak çoklu dönüş değerleri var.
ANeves

4

İşte temel Twoyöntemler:

1) 'Kullanımıout Parametre olarak ' kullanımı Hem 4.0 hem de küçük sürümler için de 'out' kullanabilirsiniz.

'Out' örneği:

using System;

namespace out_parameter
{
  class Program
   {
     //Accept two input parameter and returns two out value
     public static void rect(int len, int width, out int area, out int perimeter)
      {
        area = len * width;
        perimeter = 2 * (len + width);
      }
     static void Main(string[] args)
      {
        int area, perimeter;
        // passing two parameter and getting two returning value
        Program.rect(5, 4, out area, out perimeter);
        Console.WriteLine("Area of Rectangle is {0}\t",area);
        Console.WriteLine("Perimeter of Rectangle is {0}\t", perimeter);
        Console.ReadLine();
      }
   }
}

Çıktı:

Dikdörtgen Alanı 20

Dikdörtgenin Çevresi 18

* Not: *out -keyword, gerçek değişken konumları çağrılan yöntemin yığınına kopyalanan ve aynı konumların yeniden yazılabildiği parametreleri açıklar. Bu, çağıran yöntemin değiştirilen parametreye erişeceği anlamına gelir.

2) Tuple<T>

Tuple örneği:

Kullanarak Birden Çok DataType değeri döndürme Tuple<T>

using System;

class Program
{
    static void Main()
    {
    // Create four-item tuple; use var implicit type.
    var tuple = new Tuple<string, string[], int, int[]>("perl",
        new string[] { "java", "c#" },
        1,
        new int[] { 2, 3 });
    // Pass tuple as argument.
    M(tuple);
    }

    static void M(Tuple<string, string[], int, int[]> tuple)
    {
    // Evaluate the tuple's items.
    Console.WriteLine(tuple.Item1);
    foreach (string value in tuple.Item2)
    {
        Console.WriteLine(value);
    }
    Console.WriteLine(tuple.Item3);
    foreach (int value in tuple.Item4)
    {
        Console.WriteLine(value);
    }
    }
}

Çıktı

perl
java
c#
1
2
3

Not: Tuple kullanımı, Framework 4.0 ve sonraki sürümleri için geçerlidir . Tupletürü bir class. Bellekte yönetilen yığın üzerinde ayrı bir yere tahsis edilecektir. Bir kez oluşturduğunuzda Tuple, değerini değiştiremezsiniz fields. Bu Tupledaha çok a struct.


4
<--Return more statements like this you can --> 

public (int,string,etc) Sample( int a, int b)  
{
    //your code;
    return (a,b);  
}

Gibi kod alabilirsiniz

(c,d,etc) = Sample( 1,2);

Umarım işe yarar.


3

Bir temsilci alan bir yöntem arayan kişiye birden çok değer sağlayabilir. Bu, buradaki cevabımdan ödünç alıyor ve Hadas'ın kabul edilen cevabından biraz kullanıyor .

delegate void ValuesDelegate(int upVotes, int comments);
void GetMultipleValues(ValuesDelegate callback)
{
    callback(1, 2);
}

Arayanlar bir lambda (veya adlandırılmış bir işlev) sağlar ve intellisense, değişken adlarını temsilciden kopyalayarak yardımcı olur.

GetMultipleValues((upVotes, comments) =>
{
     Console.WriteLine($"This post has {upVotes} Up Votes and {comments} Comments.");
});

2

Sadece OOP tarzında aşağıdaki gibi bir sınıf kullanın:

class div
{
    public int remainder;

    public int quotient(int dividend, int divisor)
    {
        remainder = ...;
        return ...;
    }
}

İşlev üyesi, çoğu arayanın öncelikli olarak ilgilendiği bölümü döndürür. Ek olarak, geri kalanını, arayan tarafından daha sonra kolayca erişilebilen bir veri üyesi olarak depolar.

Bu şekilde, birçok hata mesajının gerekli olabileceği ancak yalnızca bir hata oluştuğunda veritabanı veya ağ çağrıları uygularsanız çok yararlı olan birçok "dönüş değeri" elde edebilirsiniz.

Bu çözümü OP'nin atıfta bulunduğu C ++ sorusuna da girdim.


2

Gönderen bu mesajlar yukarıda söylediği gibi yazıda, üç seçeneğiniz kullanabilirsiniz.

KeyValuePair en hızlı yoldur.

dışarı ikinci.

Kayıt düzeni en yavaş olanıdır.

Her neyse, bu senaryo için en iyi olana bağlıdır.


2

C # 'ın gelecekteki sürümü adlandırılmış tuples içerecek. Demo için bu kanal 9 oturumuna göz atın https://channel9.msdn.com/Events/Build/2016/B889

Tuple şeyler için 13: 00'a atlayın. Bu, aşağıdaki gibi şeylere izin verecektir:

(int sum, int count) Tally(IEnumerable<int> list)
{
// calculate stuff here
return (0,0)
}

int resultsum = Tally(numbers).sum

(videodan eksik örnek)


2

Dinamik bir nesne kullanabilirsiniz. Bence Tuple'dan daha iyi okunabilirliğe sahip.

static void Main(string[] args){
    var obj = GetMultipleValues();
    Console.WriteLine(obj.Id);
    Console.WriteLine(obj.Name);
}

private static dynamic GetMultipleValues() {
    dynamic temp = new System.Dynamic.ExpandoObject();
    temp.Id = 123;
    temp.Name = "Lorem Ipsum";
    return temp;
}

3
Derleme zamanı tür denetimini kaybedersiniz.
Micha Wiedenmann

1

Bunu yapmanın yolları:

1) KeyValuePair (En İyi Performans - 0,32 ns):

    KeyValuePair<int, int> Location(int p_1, int p_2, int p_3, int p_4)
    {                 
         return new KeyValuePair<int,int>(p_2 - p_1, p_4-p_3);
    }

2) Tuple - 5.40 ns:

    Tuple<int, int> Location(int p_1, int p_2, int p_3, int p_4)
    {
          return new Tuple<int, int>(p_2 - p_1, p_4-p_3);
    }

3) out (1.64 ns) veya ref 4) Kendi özel sınıfınızı / yapınızı oluşturun

ns -> nanosaniye

Referans: çoklu-dönüş-değerleri .


0

bunu deneyebilirsin

public IEnumerable<string> Get()
 {
     return new string[] { "value1", "value2" };
 }

1
Bu gerçekten birden fazla değer döndürmez . Tek bir koleksiyon değeri döndürür.
Matthew Haugen

Ayrıca, neden yield return "value1"; yield return "value2";açıkça yeni bir tane oluşturmak zorunda kalmıyorsunuz string[]?
Thomas Flinkow

0

Bir OperationResult da kullanabilirsiniz

public OperationResult DoesSomething(int number1, int number2)
{
// Your Code
var returnValue1 = "return Value 1";
var returnValue2 = "return Value 2";

var operationResult = new OperationResult(returnValue1, returnValue2);
return operationResult;
}

-7

Dizi türü için özel olarak hızlı bir yanıt döndürür:

private int[] SumAndSub(int A, int B)
{
    return new[] { A + B, A - B };
}

Kullanımı:

var results = SumAndSub(20, 5);
int sum = results[0];
int sub = results[1];

3
"Programcılar zamana ve unutulmaz yöntemlere ihtiyaç duyar" ile ne demek istiyorsun?
Thomas Flinkow

2
iki kez [0] sonuç kullandınız. bunda yanlış olanın bir belirtisi
symbiont

1
Hiç şüphe yok ki unutulmaz bir cevap
Luis Teijon
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.