2013-06-06 3 views
3

Смущенный C# делегат проходящего в качестве параметра:.NET делегат передается в качестве вопроса параметра

class Program 
{ 
    static void Main(string[] args) 
    { 
     var a = new A(); 
     Action holder = delegate{}; 
     //a.Attach1(holder); //nothing printed 
     a.Attach2(ref holder);//print as expected 

     holder(); 
    } 
} 

public class A 
{ 
    private void P1() 
    { 
     Console.WriteLine("Inaccessible"); 
    } 
    public void P2() 
    { 
     Console.WriteLine("Accessible"); 
    } 
    public void Attach1(Action holder) 
    { 
     holder += P1; 
     holder += P2; 
    } 
    public void Attach2(ref Action holder) 
    { 
     holder += P1; 
     holder += P2; 
    } 
} 

делегат ссылочного типа, почему она до сих пор нужно быть принят реф в купели, чтобы правильно работать как в Attach2, то как тип значения?

Из опыта C++ делегат является просто указателем на функцию, Attach1 (держатель действия) является чем-то вроде Attach1 (держатель Action *), исходный держатель передается как «значение», поэтому не назначается, а во втором случае, Attach2 (ref Action holder) является чем-то вроде Attach1 (держатель Action **), указатель фактически передается, поэтому его можно правильно манипулировать. Но почему в .NET нет указаний или подсказок ???

+2

Если вы думаете делегатом как «просто указатель на функцию », что (в вашей ментальной модели) делает' + = 'делать? –

ответ

6

Поскольку экземпляр делегата неизменны и += новое назначение нового экземпляра делегата в ; это в основном:

holder = (Action)Delegate.Combine(holder, P1); 
holder = (Action)Delegate.Combine(holder, P2); 

Если вы не сдадите, что, как ref, новое значение не будет видно за пределами метода.

Или, проще говоря, считать string; a string также неизменен, и += - это задание. Теперь рассмотрим:

public void Append(string s) { 
    s += "[suffix]"; 
} 
public void Append2(ref string s) { 
    s += "[suffix]"; 
} 

Если мы называем:

string x = "abc"; 
Append(x); 
Console.WriteLine(x); 

мы увидим abc. Если мы называем

string x = "abc"; 
Append2(ref x); 
Console.WriteLine(x); 

мы увидим abc[suffix] - для тех же причин.

+0

для тех из нас, кто не использовал слово неизменяемое со времен университета http://en.wikipedia.org/wiki/Immutable_object :) – Liam

2
holder += P1; 

Эта линия фактически создает новый делегат и присваивает его переменной holder. Он не изменяет существующего делегата.

Итак:

Action holder = delegate{}; 
a.Attach2(ref holder); 

holder(); //<-- holder is no longer the delegate assigned two lines above 

И, конечно же, вам нужно использовать ref, чтобы сделать эту работу, так как в противном случае назначение внутри Attach2 влияет только на то, что фактически является локальной переменной.

0

Как указано на MSDN

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

Смотрите второй пример по этой ссылке, это объясняет, постановка вопроса

0

Я полагаю, вы просто экспериментировал с этим. Вы, как правило, отправить эту функцию в том, что вы хотели, чтобы быть добавлен в качестве делегата-члена, а затем член будет новый объект возвращение + =

public class Program 
{ 
    static void Main(string[] args) 
    { 
     var a = new A(); 
     a.Attach(); 
     a.doSomething(); 
    } 
} 

public class A 
{ 
    public delegate void handler(); 
    public handler doSomething; 
    private void P1() 
    { 
     Console.WriteLine("Inaccessible"); 
    } 
    public void P2() 
    { 
     Console.WriteLine("Accessible"); 
    } 
    public void Attach() 
    { 
     doSomething += P1; 
     doSomething += P2; 
    } 
} 
Смежные вопросы