2015-04-08 2 views
0

Мы столкнулись с проблемой, связанной с продвижением по службе, и я надеюсь, что смогу объяснить ее кодом. Я хочу понять, почему он ведет себя так, как он есть.Попытка понять изменения подписи метода, охватывающие сборки

Ассамблея 1

public static class Foo 
{ 
    public static string DoStuff() 
    { 
     // Do something 

     return "some string"; 
    } 
} 

Ассамблея 2:

public class Bar 
{ 
    public void SomeMethod() 
    { 
     // I realize the below is not what should be done for catching exceptions, as the exception is never thrown due to the return, and seems unnecessary either way... 
     // this is inherited code and has not been modified to correct. 

     try 
     { 
      var someValue = Foo.DoStuff(); 
     } 
     catch (Exception) 
     { 
      return; 
      throw; 
     } 
    } 
} 

требования изменились так, что DoStuff необходимо будет принимать в качестве параметра, значение которого будет немного изменить поведение. Обратите внимание, что изменяется только сборка 1.Foo.

Новый Foo

public static class Foo 
{ 
    public static string DoStuff(bool someBool = false) 
    { 
     // Do something 

     return "some string"; 
    } 
} 

Это Перекомпилирован хорошо, и сборка 2 был в состоянии успешно использовать измененную сигнатуру метода без жалоб. Моя проверка была выполнена, и проектные DLL, у которых были изменения, были повышены (обратите внимание, что это была только сборка 1-й сборки).

После раскрутки мы обнаружили, что сборка 2 не срабатывала по вызову Foo.DoStuff() - к сожалению, я не могу предоставить исключение, поскольку приведенный выше код проглотил его.

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

Я использовал функцию dotnet peek, чтобы заглянуть в «старую dll» и «новые dll» (даже если код не изменился на эту сборку и не заметил разницу в двух DLL в том, что в старой DLL, значение параметра по умолчанию не был поставлен, но в перекомпиляции параметр значение по умолчанию подавался

Я думаю, мой вопрос (ы) сводятся к следующему:.

  1. Почему скомпилированный код на изменения в сборе 2 основанный на сигнатуре метода, который (по крайней мере, я думаю) должен быть прозрачным?
  2. Будет ли метод избегать такая ситуация просто «развернуть все»? Вместо того, чтобы пытаться развернуть на основе изменений кода? Обратите внимание, что библиотеки DLL не проверяются, так что не было никакого фактического «изменение кода» для сборки 2, хотя DLL отличается на основе изменений в Ассамблее 1.
+1

Другим способом избежать этой ситуации было бы оставить метод с параметрами без изменений и вызвать его новый метод со значением по умолчанию, а не указывая значение по умолчанию в сигнатуре метода: 'public static string DoStuff() {return DoStuff (false); } '. Также было бы полезно удалить «возврат» из этого улова (или, по крайней мере, занести в журнал исключение). –

+0

@RufusL Если бы я мог все это сделать, зная, что я знаю сейчас, это определенно было бы тем подходом, который я бы взял;) – Kritner

ответ

2

Почему скомпилированный код по сборке 2 изменение на основе сигнатуры метода, которое (по крайней мере, я думаю) должно быть прозрачным?

Нет, не следует. Если вы не укажете аргумент для соответствия с необязательным параметром, значение по умолчанию будет указано на сайте вызова - то есть в сборке 2 в вашем случае. Вот как работают дополнительные параметры в C#. Это боль, но это жизнь.

Может ли метод избежать подобной ситуации просто «развернуть все»?

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

Вы также можете увидеть такие незначительные изменения, как в других случаях, - там, где тот же старый «клиентский» код компилируется с новым «получающим» кодом, но имеет другое значение. Два примера:

  • Предположим, что вы изменить сигнатуру метода от Foo(long x) к Foo(int x) но на месте вызова, вы только называют его Foo(5) ... что составляет в обоих случаях, но с другим кодом.

  • Предположим, у вас есть изменить метод подписи от Foo(int x, int y) к Foo(int y, int x) и у вас есть вызов Foo(x: 5, y: 2) ... опять же, смысл изменений кода от Foo(5, 2) к Foo(2, 5), если вы видите, что я имею в виду. Повторная компиляция неизмененного исходного кода с новым «получающим» кодом изменит поведение.

  • Предположим, что у вас есть постоянная в сборе 1, например. public const string Foo = "Foo";. Если вы измените это на public const string Foo = "Bar", но не перекомпилируйте сборки с использованием константы, эти сборки все равно будут использовать значение «Foo». (Это также относится к значениям по умолчанию для дополнительных параметров.)

+0

Было бы лучшим решением оставить версию «DoStuff()» без параметров место и вызвать вызов параметризованной перегрузки со значением по умолчанию? Что-то вроде: 'public static string DoStuff() {return DoStuff (false); } '? Или у этого есть собственный набор проблем? –

+0

@RufusL: Это более совместимый с обратной стороной способ сделать это, да - это действительно зависит от того, действительно ли перераспределение всего на самом деле является проблемой. –

+0

спасибо, я раньше не думал о ваших сценариях «предположим», что помогает! – Kritner

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