2014-01-28 2 views
1

Предположим, я следующий код WCF:Как передать несколько методов (с параметрами) как параметр?

try 
    { 
     ServiceClient proxy = new ServiceClient(); 
     proxy.ClientCredentials.UserName.UserName = "user"; 
     proxy.ClientCredentials.UserName.Password = "password"; 
     proxy.GetData(2); 
     if (proxy.State = CommunicationState.Opened) 
     { 
      proxy.GetData("data"); 
     } 
     proxy.Close(); 
    } 
    catch (FaultException ex) 
    { 
     // handle the exception  
    } 

И так как я заметил, что попытка ... поймать и другая логика повторяется, не говоря уже о том, что создание WCF вызов дорого, я хочу отправить многие «методы и параметры» к этой функции.

По существу проходят GetData(2) и GetData("data") как массив методов, и результаты возвращаются либо асинхронно, либо синхронно.

Как бы это осуществить?

Я полагаю, что для обработки результатов [] и общей блокировки результатов [] можно было бы иметь два объекта «ref». Однако я не уверен, как передавать «методы с параметрами» в качестве параметра другой функции.

Возможно, другим способом взглянуть на это может быть массив указателей на функции, с той же функцией с разными параметрами.

Может ли кто-нибудь подтолкнуть меня к правильному пути?

Подробнее:

Я задаю этот вопрос so I can optimize this approach to handling WCF exceptions and retries но так что я не всегда должен открыть/закрыть клиент после каждого вызова.

ответ

7

Используйте делегат и передать их в списке.

Делегат C# Func<T> используется, когда требуется возвращаемое значение.

List<Func<Data>> funcList = new List<Func<Data>>(); 
funcList.Add(() => GetData(2)); 

// You can use any condition as you otherwise would to add to the list. 
if (proxy.State = CommunicationState.Opened) 
{ 
    funcList.Add(() => GetData("data")); 
} 

List<Data> ProcessFuncs(List<Func<Data>> funcDatas) 
{ 
    List<Data> returnList = new List<Data>(); 
    foreach(var func in funcDatas) 
    { 
     returnList.Add(func()); 
    } 
} 

(до тех пор, как возвращаемые типы идентичны, это будет работать)

Это просто пример, конечно; если ваши методы ничего не возвращают, вы можете использовать делегат C# Action, который просто выполняет действие и не возвращает никакого значения.

List<Action> actionList = new List<Action>(); 
actionList.Add(() => ProcessData("data")); // ProcessData is a void with no return type 
actionList.Add(() => ProcessData(2)); 

public void ProcessActions(List<Action> actions) 
{ 
    foreach(var action in actions) 
    { 
     action(); 
    } 
} 

В ответ на некоторые комментарии:

Этот код компилируется и эквивалентны:

class Program 
{ 
    public static string GetData(string item) { return item; } 
    public static string GetData(int item) { return item.ToString(); } 

    static void Main(string[] args) 
    { 
     string someLocalVar = "what is it?"; 
     int someLocalValueType = 3; 

     Func<string> test =() => 
     { 
      return GetData(someLocalVar); 
     }; 

     Func<string> test2 =() => GetData(someLocalValueType); 
     someLocalValueType = 5; 

     List<Func<string>> testList = new List<Func<string>>(); 

     testList.Add(() => GetData(someLocalVar)); 
     testList.Add(() => GetData(2)); 
     testList.Add(test); 
     testList.Add(test2); 

     someLocalVar = "something else"; 

     foreach(var func in testList) 
     { 
      Console.WriteLine(func()); 
     } 

     Console.ReadKey(); 
    } 
} 

Результат:

enter image description here

+0

Вы даже можете использовать версию Action, если они * делают * что-то возвращать. Вы можете включить назначение в лямбда. Это может быть опасно, но иногда это может быть то, что вы хотите. – Magus

+0

Да, или вы можете также иметь {} тело для своего действия вместо лямбды и делать что-нибудь в нем, а затем ничего не возвращать из действия. Много возможностей. –

+0

Я что-то пропустил, потому что не думаю, что код даже компилируется ... Я получаю «Делегат» System.Func «не принимает 1 аргумент». Я уверен, что вы не можете просто добавлять делегатов в список с передаваемыми аргументами, как если бы вы вызывали метод ... – evanmcdonnal

0

Вы могли бы использовать делегаты C#:

Делегат представляет собой тип, который представляет ссылки на методы с определенным списком в параметров и тип возвращаемого значения. Когда вы создаете экземпляр делегата , вы можете связать его экземпляр любым методом с помощью типа подписи и возвращаемого типа . Вы можете вызвать (или вызвать) метод через экземпляр делегата. Делегаты используются для передачи методов в качестве аргументов другим методам. Обработчики событий не более , чем методы, вызываемые через делегатов. Вы создаете настраиваемый метод , и класс, такой как элемент управления Windows, может вызвать ваш метод при возникновении определенного события. Следующий пример показывает, делегат заявления:

Подробнее об этом: http://msdn.microsoft.com/en-us/library/ms173171.aspx

1

пыльник является примером того, как чтобы сделать коллекцию делегатов и их аргументы, затем вызвать t позже, не зная определения методов. Насколько я знаю, если вы хотите вызывать методы с разными определениями в одном общем вызове, вам нужно сделать что-то вроде этого.

List<Tuple<delegate, object[]>> delegates = new List<Tuple<delegate, object[]>>(); 

    delegates.Add(new Tuple<delegate, object[]>(new Func<Arg1Type, Arg2Type, ReturnType>(MyFunctionName), new object[] { arg1, arg2 }); 

    foreach (Tuple<delegate, object[]> d in delegates) 
    { 
     d.Item1.DynamicInvoke(d.Item2); 
    } 
+0

Я думаю, что я предпочел бы просто создать коллекцию «Действие» и передать аргументы и функции в лямбда. Может быть, чище. – Magus

+0

@Magus мой код немного неряшлив, они должны быть 'Func', потому что у них есть тип возвращаемого значения, я просто не назначаю его нигде.Я не знаю об этом коде, передающем args в лямбда, насколько я могу судить, он не компилируется. Собственно, 100% он не компилируется. Когда вы говорите 'List >' a и b - args, а c - тип возвращаемого значения. Он объявляет эти функции с возвращаемым типом 'Data', затем пытается передать им случайные аргументы, которые он не сказал, что они возьмут ... Вы должны использовать массив объектов и' DynamicInvoke', чтобы получить такое поведение. – evanmcdonnal

+0

Я имел в виду строки 'string b; Действие a =() => b = SomeFunction (param1, param2); 'где b должно быть полем. Это довольно чисто, и делает то же самое. – Magus

0

Вы можете передать функции с параметрами таким образом:

public void strategy<R, T1, T2>(Func<R, T1, T2> f); 

public bool predicate(string a, string b); 

strategy<bool, string, string>(predicate); 

Первая строка объявляет функцию strategy() принимающую функцию f; Эта функция возвращает тип R и принимает два параметра типа T1 и T2.

Вторая строка определяет функцию, которая возвращает bool и принимает два string.

Третья строка вызывает стратегию, передавая ей предикат в качестве параметра.

0

Не уверены, чтобы понять, что вы пытаетесь достичь, но в основном, если ваша служба предоставляет метод GetData(int) и метод GetData(string), а также асинхронный прокси, вы должны назвать как асинхронно, используя что-то вроде:

var getData = proxy.GetDataAsync(2); 
var getData2 = proxy.GetDataAsync("data"); 

await Task.WhenAll(getData, getData2); 

// Gets the result using getData.Result...etc. 
1

Я бы не использовал делегатов здесь, потому что тогда вы ограничены типами и решаете, что это становится ужасным и чрезмерно сложным. Я бы просто получил обратный вызов, который дает вам свободное владение над ServiceClient, как только он будет настроен. Я думаю, что это шаблон, который имеет имя, но я не знаю.

interface IProxyActionCallback 
{ 
    void DoProxyStuff(ServiceClient proxy); 
} 

void MyMethod(IProxyActionCallback callback) 
{ 
    try 
    { 
     ServiceClient proxy = new ServiceClient(); 
     proxy.ClientCredentials.UserName.UserName = "user"; 
     proxy.ClientCredentials.UserName.Password = "password"; 

     callback.DoProxyStuff(proxy); 

     proxy.Close(); 
    } 
    catch (FaultException ex) 
    { 
     // handle the exception  
    } 
} 

Затем вызывается метод, как:

MyMethod(new DoSpecificStuff()); 

Где DoSpecificStuff это класс, который реализует интерфейс и позволяет выполнять определенные вызовы с помощью прокси-сервера:

class DoSpecificStuff : IProxyActionCallback 
{ 
    public void DoProxyStuff(ServiceClient proxy) 
    { 
     proxy.GetData(2); 
     if (proxy.State = CommunicationState.Opened) 
     { 
      proxy.GetData("data"); 
     } 
    } 
} 

Так вы У вас есть тонны классов, которые реализуют интерфейс, и все они «разделяют» один и тот же файл прокси-сервера с пробкой, который находится в одном месте.

Смежные вопросы