2012-01-17 1 views
1

Похоже, что я пропустил что-то очевидное, но на самом деле не могу понять, почему я не могу использовать/получить доступ к методу, который был присвоен переданному в Func<> параметр, я хочу получить доступ/вызвать его во внешнем контексте вызова.Доступ к методу, который был назначен переданному в параметре Func <>

Ниже простого пример: (реальный results типа данных не string но некоторый сложный тип)

class Caller 
{ 
    private Func<string> factory; 

    public Caller() 
    { 
     this.factory = null; 
     var adapter = new Adapter(this.factory); 
     adapter.Adapt(...); 

     // then I would share this factory via IoC 
     dependencyContainer.RegisterUsingFactoryMethod<string>(this.factory); 

     // !!! but here is factory still not assigned 
     string results = this.factory(); 
    } 
} 

class Adapter 
{ 
    private string results; 

    public Adapter(Func<string> factory) 
    { 
     factory =() => this.results; 
    } 

    public IOut Adapt(IIn input) 
    { 
     this.results = someCalculatedData; 
    } 
} 
+2

пытался использовать 'out'? – Reniuz

ответ

4

Это не имеет ничего общего с делегатами, и все, что связано с тем фактом, что все аргументы передаются по значению по умолчанию. Вы увидите то же поведение, если factory были int, или string, или StringBuilder.

Если вы хотите, чтобы ваш конструктор Service изменил значение аргумента, вам нужен параметр ref или out.Тем не менее, я лично полагаю, что вы изменить его, чтобы использовать метод, который возвращаетFunc<string>:

public class Caller 
{ 
    private readonly Func<string> factory; 

    public Caller() 
    { 
     this.factory = Service.GetFunc(); 
    } 
} 

public class Service 
{ 
    public static Func<string> GetFunc() 
    { 
     return() => Guid.NewGuid().ToString(); 
    } 
} 

(Существуют и другие варианты здесь, такие как преобразование метод групповой, или свойство Это трудно. знать, что рекомендовать, когда у нас не так много контекста.)

Очевидно, что вы хотите переименовать метод. Но вызов конструктора исключительно для изменения параметра out был бы очень странным битом кода.

Подробнее об отправке параметров см. В разделе article on the topic.

+0

+1 и спасибо за предоставление различных решений с интересными проблемами, действительно я знаю, что просто может выставить метод, но это немного испортит API службы. Идея, что услуга на определенном этапе (а не строительство, как в примере), способна инициализировать завод, поэтому я могу использовать его позже, действительно я не уверен, что, возможно, общий дизайн с этим подходом неверен – sll

+0

@sll: Но почему вы * хотите * построить объект 'Service'? Вы должны быть заинтересованы в том, что это на самом деле обеспечивает. Каждый раз, когда вы вызываете конструктор и полностью игнорируете вновь созданный объект, вы должны быть обеспокоены. –

+0

Хорошо, что это не сервис, а какой-то адаптер, и в то время как преобразование данных должно кэшировать некоторые промежуточные данные для будущего использования, isea должен был зарегистрировать эти данные в контейнере IoC, но для этого требуется впрыск IoC в адаптер, и я не как и эта идея, но теперь это выглядит не так уж плохо, чем инъекция 'ref Func' – sll

2

Вы никогда не назначить на завод в вызываемом абоненте,

Поскольку вы не пропуская завод к службе по ссылке обновления к переменной не будут отображаться вызывающим абонентом.

я не уверен, может ли вы передать параметры в застройщик по ссылке или нет ...

+1

+1, так как вы правы, что добавление модификатора 'ref' решило бы проблему – sll

+0

(просто, чтобы объяснить мой отредактированный и удаленный комментарий - я бы неправильно понял ответ, сказав, что он * передавал по ссылке.) –

0

Для вызова Func<T...> ссылки, вам просто нужно позвонить, как если бы это был обычный метод. т.е .:

class Service 
{ 
    public Service(Func<string> factory) 
    { 
     string result = factory(); 
    } 
} 

Обратите внимание, что Func<string> возвратит string типа.

EDIT

Ваш комментарий я понял, что ты на самом деле с просьбой в ОП. Чтобы иметь возможность присвоить значение любому параметру, вам необходимо пометить его out или ref. Так что ваше объявление будет выглядеть так:

public Service(out Func<string> factory) 

или

public Service(ref Func<string> factory) 
+0

Хорошо, но я хотите вызвать фабрику в «Caller», поэтому сервис просто предоставляет какой-либо завод вызывающей программе. Я могу выставить метод и использовать его, но это другой подход, который я не хочу придерживаться – sll

+0

@sll Отредактировал свой ответ. –

3

Вы можете попробовать Пазинг в FUNC с из параметра конструктору службы?

например.

class Caller 
{ 
    private Func<string> factory; 

    public Caller() 
    { 
     this.factory = null; 
     var service = new Service(out this.factory); 

     // !!! but here is factory still not assigned 
     string results = this.factory(); 
    } 
} 

class Service 
{ 
    public Service(out Func<string> factory) 
    { 
     factory =() => Guid.NewGuid().ToString(); 
    } 
} 

Это гарантирует, что компилятор знает, для инициализации ссылки на заводские параметры в конструкторе Service. В противном случае вы просто передаете ссылку на нуль Func на конструктор по значению, а затем назначаете функцию этой переменной в стек внутри ctor.

Для уточнения параметров ref и out см. this related question.

С наилучшими пожеланиями,

+3

Это будет работать, но я не думаю, что это хороший дизайн. Зачем вызывать конструктор * just * для изменения значения параметра out? –

+0

@JonSkeet согласился. Лично у меня был бы метод с сигнатурой, такой как «Func Service.InitializeCallback()» или «Service.InitializeCallback (out Func callback)», чтобы явно создать делегат. –

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

  • Нет связанных вопросов^_^