2013-04-04 3 views
0

Вот мой код:Почему следующая статическая функция не работает?

public static class s { 

    public static int Dx(this int i, Action<int> act, Func<int, bool> con) { 
     if (con(i)) act(i); 
     return i; 
    } 
} 

Позже В моем коде я:

int g = 22; 
int false_con = g.Dx(j => j = 11, z => z != 22); // This is 22 which is fine. 
int true_con = g.Dx(j => j = 11, z => z == 22); //This is also 22 which should be 11 

Как это исправить?

+1

Похоже, что простой тип ссылочного типа/значения недопонимания. –

+3

@AdrianGodong: Нет, это не имеет никакого отношения к ссылочным типам и типам значений. Это связано с тем, что переменные передаются по значению независимо от того, ограничена ли переменная ссылочным типом или типом значения. –

+1

@ EricLippert Справа, меня путают между (by-) ref и ref-type. –

ответ

16

Ни один из ответов, приведенных до настоящего момента, не является фактическим объяснением.

Ряд ответов, приведенных до сих пор, указывает, что это происходит потому, что int является типом значения. Попробуйте заменить все int s на object s в этой программе. Он начинает работать так, как ожидает первоначальный плакат? Нет. Тогда это не имеет никакого отношения к типам значений против ссылочных типов.

Скорее, это связано со значением переменной , независимо от его типа.

Махди, ваше ожидание, что формальный параметр j обретает значение псевдоним для формального параметра i, который в свою очередь является псевдонимом для локальной переменной g, и поэтому любое изменение j также вызывает изменения в g, потому что они той же переменной. Это не так. j, i и g являются копий того же значения, но имеют разные места хранения, поэтому мутирующий один не мутирует другой.

Как вы говорите, «этот формальный параметр является псевдонимом для этой переменной» в C#, используя ключевые слова ref или out.Так эта программа будет делать то, что вы ожидаете:

delegate void RefAction<T>(ref T t); 
... 
public static int Dx(ref int i, RefAction<int> act, Func<int, bool> con) 
{ 
    if (con(i)) 
     act(ref i); 
    return i; 
} 
... 
int g = 22; 
int false_con = Dx(ref g, (ref int j) => { j = 11; }, z => z != 22); 
int true_con = Dx(ref g, (ref int j) => { j = 11; }, z => z == 22); 

В «реф» ключевые слова означают, что g, i и j все разные названия одной и той же переменной.

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

+0

+1 Вы совершенно правы, как обычно –

+0

Спасибо за ваше блестящее объяснение +1 –

1

Ваш код возвращает целое число, на которое он вызывается, поэтому оба случая возвращают g, который равен 22.

3

Ваш путь код не изменяется в результате вызова Action, поэтому программа всегда возвращается i.

Похоже, что вы ожидаете g быть изменен на 11 после первого вызова, которая не соответствует действительности, так как значение д копируется j, а затем i, а не ссылка к g, означая значение в g не изменяется в результате вашего действия.

Одним из вариантов было бы возвращения величина вместо того, чтобы пытаться изменить это:

public static int Dx(this int i, Func<int, int> act, Func<int, bool> con) { 
    if (con(i)) return act(i); 
    return i; 
} 

int g = 22; 
g = g.Dx(j => 11, z => z != 22); /* g is still 22 */ 
g = g.Dx(j => 11, z => z == 22); /* g is now 11 */ 
+0

', что не так, поскольку значения типов передаются значением. ** Все ** объекты передаются значениями, если вы не используете ключевое слово' ref', и в этом случае тип передается по ссылке, независимо от того, является ли это значением или ссылочного типа. Это делегат (в данном случае «Action », который определяет, проходит ли параметр по ссылке или по значению. Вы также можете определить нового делегата, который передает параметр по ссылке (хотя я бы сказал, что шаблон кода, который вы используете используется намного лучше). – Servy

+0

@Servy - да, с параметром 'ref' в делегате, мне показалось немного неприятным. –

+0

Опять же, решение, которое у вас есть, скорее всего, в чем он должен быть, но ваше объяснение почему он должен использовать его, содержит упущения и ошибки. – Servy

0

Вы пытаетесь изменить значение g через Action<int> act, верно?

Он не будет работать, поскольку целое число является примитивным типом, который передается как значение, а не как ссылка, поэтому вы фактически не назначаете какое-либо значение g.

+2

Ваши объяснения неверны. Предположим, вы изменили каждый int на объект в этой программе. Это все равно будет юридическая программа. начать работать, потому что теперь есть только ссылочные типы? –

+0

@EricLippert Ха-ха броский вопрос, так как 3 человека думали то же самое. anks Eric, я стою исправлено. –