C # kullanarak Parametre Olarak Geçiş Yöntemi


695

Hepsi aynı imza (parametreler ve dönüş değerleri) ile birkaç yöntem var ama farklı adlar ve yöntemlerin iç farklı. Ben geçirilen yöntemi çağıracak başka bir yöntem çalıştırmak için yöntemin adını geçmek istiyorum.

public int Method1(string)
{
    ... do something
    return myInt;
}

public int Method2(string)
{
    ... do something different
    return myInt;
}

public bool RunTheMethod([Method Name passed in here] myMethodName)
{
    ... do stuff
    int i = myMethodName("My String");
    ... do more stuff
    return true;
}

public bool Test()
{
    return RunTheMethod(Method1);
}

Bu kod çalışmıyor ama ben yapmaya çalışıyorum budur. Ne anlamıyorum parametre tanımlamak gerektiğinden nasıl RunTheMethod kodu yazmaktır.


12
Neden yöntemin adı yerine bir temsilci iletmiyorsunuz?
Mark Byers

Yanıtlar:


854

RunTheMethod yönteminizde parametre olarak .net 3.5'deki Func temsilcisini kullanabilirsiniz. Func delegesi, belirli bir türde birkaç parametre alan ve belirli bir türde tek bir bağımsız değişken döndüren bir yöntem belirtmenize olanak tanır. İşte çalışması gereken bir örnek:

public class Class1
{
    public int Method1(string input)
    {
        //... do something
        return 0;
    }

    public int Method2(string input)
    {
        //... do something different
        return 1;
    }

    public bool RunTheMethod(Func<string, int> myMethodName)
    {
        //... do stuff
        int i = myMethodName("My String");
        //... do more stuff
        return true;
    }

    public bool Test()
    {
        return RunTheMethod(Method1);
    }
}

51
Metod, void döndürme imzası olarak ve parametre içermiyorsa Func çağrısı nasıl değişecekti? Sözdiziminin çalışmasını sağlayamıyorum.
user31673

211
@unknown: Bu durumda bunun Actionyerine olurdu Func<string, int>.
Jon Skeet

12
ama şimdi ne yöntem argüman geçirmek istiyorsanız ??
john ktejik

40
@ user396483 Örneğin, Action<int,string>2 parametre (int ve string) alan ve void döndüren bir yönteme karşılık gelir.
serdar

24
@NoelWidmer Use Func<double,string,int>, 2 parametre ( doubleve string) alan ve geri dönen bir yönteme karşılık gelir int. Son belirtilen tür, dönüş türüdür. Bu temsilci 16 parametreye kadar kullanabilirsiniz. Bir şekilde daha fazlasına ihtiyacınız varsa, kendi temsilcinizi olarak yazın public delegate TResult Func<in T1, in T2, (as many arguments as you want), in Tn, out TResult>(T1 arg1, T2 arg2, ..., Tn argn);. Yanlış anladıysam lütfen beni düzeltin.
serdar

356

Bir temsilci kullanmanız gerekir . Bu durumda, tüm yöntemleriniz bir stringparametre alır ve bir döndürür int- bu en basit olarak Func<string, int>delege 1 tarafından temsil edilir . Böylece kodunuz bu kadar basit bir değişiklikle doğru hale gelebilir:

public bool RunTheMethod(Func<string, int> myMethodName)
{
    // ... do stuff
    int i = myMethodName("My String");
    // ... do more stuff
    return true;
}

Delegelerin kabul ettiği gibi bundan çok daha fazla gücü var. Örneğin, C # ile lambda ifadesinden bir temsilci oluşturabilirsiniz , böylece yönteminizi şu şekilde çağırabilirsiniz:

RunTheMethod(x => x.Length);

Bu böyle anonim bir işlev yaratacaktır:

// The <> in the name make it "unspeakable" - you can't refer to this method directly
// in your own code.
private static int <>_HiddenMethod_<>(string x)
{
    return x.Length;
}

ve sonra da delegeyi RunTheMethodyönteme geçirin.

Etkinlik abonelikleri, eşzamansız yürütme, geri aramalar - her türlü şey için temsilci kullanabilirsiniz. Özellikle LINQ kullanmak istiyorsanız, onları okumaya değer. Bir var makale olduğunu çoğunlukla delege ve olaylar arasındaki farklar hakkında, ancak bunu yararlı Neyse bulabilirler.


1 Bu sadece Func<T, TResult>çerçevedeki genel delege türünü temel alır ; kolayca kendiniz beyan edebilirsiniz:

public delegate int MyDelegateType(string value)

parametrenin türünü yazın MyDelegateType.


59
+1 Bu gerçekten iki dakika içinde tıkırtı için inanılmaz bir cevap.
David Hall

3
İşlevi delegeler kullanarak geçirebilmenize rağmen, daha geleneksel bir OO yaklaşımı strateji modelini kullanmak olacaktır.
Paolo

21
@Paolo: Delegeler, söz konusu stratejinin yalnızca tek bir yöntem gerektirdiği strateji modelinin çok uygun bir uygulamasıdır. Bu strateji modeline aykırı değil - ama modeli arayüzler kullanarak uygulamaktan çok daha kullanışlı.
Jon Skeet

5
"Klasik" delegeler (.NET 1/2'den bilindiği gibi) hala yararlı mı, yoksa Func / Action nedeniyle tamamen eski mi? Ayrıca, örneğinizde bir temsilci anahtar kelimesi eksik değil public **delegate** int MyDelegateType(string value)mi?
M4N

1
@ Martin: Doh! Düzeltme için teşekkürler. Düzenlenen. Kendi delegelerinizi bildirmek için - tür adına bir anlam vermek yararlı olabilir, ancak .NET 3.5'ten beri nadiren kendi delege türümü oluşturdum.
Jon Skeet

113

OP örneğinden:

 public static int Method1(string mystring)
 {
      return 1;
 }

 public static int Method2(string mystring)
 {
     return 2;
 }

Action Delegate'i deneyebilirsiniz! Ve sonra yönteminizi kullanarak

 public bool RunTheMethod(Action myMethodName)
 {
      myMethodName();   // note: the return value got discarded
      return true;
 }

RunTheMethod(() => Method1("MyString1"));

Veya

public static object InvokeMethod(Delegate method, params object[] args)
{
     return method.DynamicInvoke(args);
}

Sonra yöntemi çağırın

Console.WriteLine(InvokeMethod(new Func<string,int>(Method1), "MyString1"));

Console.WriteLine(InvokeMethod(new Func<string, int>(Method2), "MyString2"));

4
Teşekkürler, bu beni birden çok parametre için izin verecek daha genel bir "RunTheMethod" yöntemi istediğimden beri gitmek istediğim yere geldi. Btw ilk InvokeMethodlambda çağrısı RunTheMethodyerine olmalı
John

1
John gibi, bu gerçekten bir hareket genel yöntemine sahip olmamı sağladı. Teşekkürler!
ean5533

2
Benim günümü yaptınız;) Seçilen cevap IMO'dan gerçekten basit ve çok daha esnek.
Sidewinder94

RunTheMethod (() => Yöntem1 ("MyString1")) üzerinde genişletmenin bir yolu var mı; bir dönüş değeri almak için? İdeal olarak jenerik?
Jay

parametreleri geçmek istiyorsanız bunun farkında olun: stackoverflow.com/a/5414539/2736039
Ultimo_m

31
public static T Runner<T>(Func<T> funcToRun)
{
    //Do stuff before running function as normal
    return funcToRun();
}

Kullanımı:

var ReturnValue = Runner(() => GetUser(99));

6
Çok kullanışlı. Bu şekilde, bir veya daha fazla parametre kullanabilirsiniz. Sanırım, en güncel cevap bu.
bafsar

Bu uygulama hakkında bir şey eklemek istiyorum. Geçireceğiniz yöntemin dönüş türü geçersizse, bu çözümü kullanamazsınız.
Imants Volkovs

@ImantsVolkovs Bunu Func yerine bir Action kullanmak için değiştirebileceğiniz ve imzayı void olarak değiştirebileceğinize inanıyorum. % 100 emin değilim.
Shockwave

2
Parametrelerin çağrılan fonksiyona aktarılmasının bir yolu var mı?
Jimmy

17

Mümkün olduğu kadar eksiksiz bir çözümü paylaşmak için, üç farklı yol sunmaya başlayacağım, ama şimdi en temel prensipten başlayacağım.


Kısa bir giriş

C #, F # ve Visual Basic gibi CLR'nin ( Ortak Dil Çalışma Zamanı ) üstünde çalışan tüm diller , kodu C ve C ++ gibi yerel dillerden (doğrudan makineye derleyen) daha yüksek bir seviyede çalıştıran bir VM altında çalışır kodu). Sonuç olarak, yöntemler herhangi bir derlenmiş blok değildir, ancak bunlar CLR'nin tanıdığı yapılandırılmış öğelerdir. Bu nedenle, bir yöntemi parametre olarak geçirmeyi düşünemezsiniz, çünkü yöntemler ifade olmadığı için herhangi bir değer üretmezler! Bunun yerine, oluşturulan CIL kodunda tanımlanan ifadelerdir. Delege konseptiyle karşılaşacaksınız.


Delege nedir?

Bir temsilci, bir yöntemin işaretçisini temsil eder. Yukarıda söylediğim gibi, bir yöntem bir değer değildir, dolayısıyla CLR dillerinde Delegateherhangi bir yöntemi tamamlayan özel bir sınıf vardır .

Aşağıdaki örneğe bakın:

static void MyMethod()
{
    Console.WriteLine("I was called by the Delegate special class!");
}

static void CallAnyMethod(Delegate yourMethod)
{
    yourMethod.DynamicInvoke(new object[] { /*Array of arguments to pass*/ });
}

static void Main()
{
    CallAnyMethod(MyMethod);
}

Üç farklı yol, aynı konsept:

  • Yol 1 Özel sınıfı doğrudan yukarıdaki örnek olarak
    kullanın Delegate. Bu çözümün sorunu, argümanlarınızı yöntem tanımındaki türlerle sınırlandırmadan dinamik olarak ilettiğinizde kodunuzun işaretlenmemesidir.

  • Yol 2 Özel sınıfın
    yanı sıra, Delegatedelege kavramı, delegateanahtar kelimeden önce gelen yöntemlerin tanımı olan özel delegelere yayılır ve normal yöntemlerle aynı şekilde davranırlar. Bu şekilde kontrol edilirler, böylece kusursuz bir kod elde edersiniz.

    İşte bir örnek:

    delegate void PrintDelegate(string prompt);
    
    static void PrintSomewhere(PrintDelegate print, string prompt)
    {
        print(prompt);
    }
    
    static void PrintOnConsole(string prompt)
    {
        Console.WriteLine(prompt);
    }
    
    static void PrintOnScreen(string prompt)
    {
        MessageBox.Show(prompt);
    }
    
    static void Main()
    {
        PrintSomewhere(PrintOnConsole, "Press a key to get a message");
        Console.Read();
        PrintSomewhere(PrintOnScreen, "Hello world");
    }
    
  • Yol 3
    Alternatif olarak, .NET Standardı'nda önceden tanımlanmış bir temsilci kullanabilirsiniz:

    • Actionvoidhiçbir argüman içermeyen bir a .
    • Action<T1>a'yı voidbir argümanla tamamlar .
    • Action<T1, T2>a'yı voidiki argümanla tamamlar .
    • Ve bunun gibi...
    • Func<TR>işlevi TRdönüş türü ve bağımsız değişken içermez.
    • Func<T1, TR>bir işlevi TRreturn türünde ve bir argümanla tamamlar .
    • Func<T1, T2, TR>bir işlevi TRreturn türünde ve iki argümanla tamamlar .
    • Ve bunun gibi...

(İkinci çözüm çoğu kişinin paylaştığı çözümdür.)


1
Geri dönüş tipi bir Func <T> olmamalı mı? Func<T1,T2,TR>
sanmcp

13

Func<string, int>Bağımsız stringdeğişken olarak alan ve aşağıdakileri döndüren bir işlevi temsil eden bir temsilci kullanmalısınız int:

public bool RunTheMethod(Func<string, int> myMethod) {
    // do stuff
    myMethod.Invoke("My String");
    // do stuff
    return true;
}

Sonra kullanın:

public bool Test() {
    return RunTheMethod(Method1);
}

3
Bu derlenmeyecek. TestYöntem olmalıdırreturn RunTheMethod(Method1);
pswg


2

Kabul edilen cevap kesinlikle doğru olsa da, ek bir yöntem sunmak istiyorum.

Benzer bir soruya kendi çözümümü aradıktan sonra buraya geldim. Eklentiye dayalı bir çerçeve oluşturuyorum ve bunun bir parçası olarak, kullanıcıların gerçek bir Menunesneyi ortaya çıkarmadan uygulamalar menüsüne genel bir listeye menü öğeleri ekleyebilmesini istedim çünkü çerçeve MenuUI olmayan diğer platformlarda dağıtılabilir itiraz ediyor. Menü hakkında genel bilgi eklemek yeterince kolaydır, ancak eklenti geliştiricisine menü tıklandığında geri arama oluşturmak için yeterli özgürlük sağlamak bir acı olduğunu kanıtlamıştır. Tekerleği ve normal menüler çağrısını yeniden icat etmeye ve olaylardan geri çağrıyı tetiklemeye çalıştığımdan şaşana kadar!

Böylece çözüm, anladıktan sonra göründüğü kadar basit, beni şimdiye kadar kaçırdı.

Mevcut yöntemlerinizin her biri için, gerekirse bir tabandan devralınan ayrı sınıflar oluşturun ve her birine bir olay işleyici ekleyin.


1

Bir işlevi parametre olarak nasıl ileteceğinizi daha iyi anlamanıza yardımcı olabilecek bir örnek.

Üst sayfanız olduğunu ve bir alt açılır pencere açmak istediğinizi varsayalım . Üst sayfada, alt pop-up metin kutusuna dayalı olarak doldurulması gereken bir metin kutusu bulunur.

Burada bir temsilci oluşturmanız gerekiyor.

Parent.cs // delege bildirimi genel delege geçersiz FillName (String FirstName);

Şimdi metin kutunuzu dolduracak bir işlev oluşturun ve işlev delegeleri eşlemelidir

//parameters
public void Getname(String ThisName)
{
     txtname.Text=ThisName;
}

Şimdi butona tıkladığınızda bir Çocuk açılır penceresi açmanız gerekir.

  private void button1_Click(object sender, RoutedEventArgs e)
  {
        ChildPopUp p = new ChildPopUp (Getname) //pass function name in its constructor

         p.Show();

    }

ChildPopUp yapıcısında, üst // sayfasının 'temsilci türü' parametresi oluşturmanız gerekir

ChildPopUp.cs

    public  Parent.FillName obj;
    public PopUp(Parent.FillName objTMP)//parameter as deligate type
    {
        obj = objTMP;
        InitializeComponent();
    }



   private void OKButton_Click(object sender, RoutedEventArgs e)
    {


        obj(txtFirstName.Text); 
        // Getname() function will call automatically here
        this.DialogResult = true;
    }

Düzenlendi, ancak bu yanıtın kalitesi hala geliştirilebilir.
SteakOverflow

1

Yöntemi parametre olarak iletmek istiyorsanız, şunu kullanın:

using System;

public void Method1()
{
    CallingMethod(CalledMethod);
}

public void CallingMethod(Action method)
{
    method();   // This will call the method that has been passed as parameter
}

public void CalledMethod()
{
    Console.WriteLine("This method is called by passing parameter");
}

0

Parametresiz bir örnek: http://en.csharp-online.net/CSharp_FAQ:_How_call_a_method_using_a_name_string

parametrelerle: http://www.daniweb.com/forums/thread98148.html#

temelde yöntem adıyla birlikte bir dizi nesne geçirirsiniz. sonra her ikisini de Invoke yöntemiyle kullanırsınız.

params Object [] parametreleri


Yöntemin adının bir dizede olmadığını unutmayın - aslında bir yöntem grubu olarak. Delegeler, yansıma değil, burada en iyi cevaptır.
Jon Skeet

@Lette: Yöntem çağrısında, bağımsız değişken olarak kullanılan ifade bir yöntem grubudur ; derleme zamanında bilinen bir yöntemin adıdır ve derleyici bunu bir temsilciye dönüştürebilir. Bu, adın yalnızca yürütme zamanında bilindiği durumdan çok farklıdır.
Jon Skeet

0
class PersonDB
{
  string[] list = { "John", "Sam", "Dave" };
  public void Process(ProcessPersonDelegate f)
  {
    foreach(string s in list) f(s);
  }
}

İkinci sınıf, depolama sınıfını kullanacak olan Client'tır. PersonDB örneğini oluşturan bir Main yöntemi vardır ve bu nesnenin Process yöntemini Client sınıfında tanımlanan bir yöntemle çağırır.

class Client
{
  static void Main()
  {
    PersonDB p = new PersonDB();
    p.Process(PrintName);
  }
  static void PrintName(string name)
  {
    System.Console.WriteLine(name);
  }
}
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.