На упражненията стана въпрос за String.Join
и реализирахме нещо подобно.
Интересно е да видим изходния код на метода, написан от Miscrosoft 😄
public static String Join(String separator, params Object[] values) {
if (values==null)
throw new ArgumentNullException("values");
Contract.EndContractBlock();
if (values.Length == 0 || values[0] == null)
return String.Empty;
if (separator == null)
separator = String.Empty;
StringBuilder result = StringBuilderCache.Acquire();
String value = values[0].ToString();
if (value != null)
result.Append(value);
for (int i = 1; i < values.Length; i++) {
result.Append(separator);
if (values[i] != null) {
// handle the case where their ToString() override is broken
value = values[i].ToString();
if (value != null)
result.Append(value);
}
}
return StringBuilderCache.GetStringAndRelease(result);
}
Няколко интересни момента.
Ако values
е празен масив (с дължина нула - values.Length == 0
) се връща празен символен низ.
values[0] == null
ще го оставим за по-надолу.
if (values.Length == 0 || values[0] == null)
return String.Empty;
В такъв случай масива съдържа поне един елемент, който се добавя към резултата:
String value = values[0].ToString();
if (value != null)
result.Append(value);
Още един интересен момент. Типа на result не е String
, а StringBuilder
. Защо се ползва StringBuilder
,
а не String
може да прочетете тук
Накратко - String
e immutable клас (https://en.wikipedia.org/wiki/Immutable_object).
Това означава, че когато конкатенирате два символни низа, се създава трети.
Ако обединявате множество низове в цикъл, какъвто е нашият случай, е по-ефективно да се използва StringBuilder
.
След това има следния цикъл:
for (int i = 1; i < values.Length; i++) {
result.Append(separator);
if (values[i] != null) {
// handle the case where their ToString() override is broken
value = values[i].ToString();
if (value != null)
result.Append(value);
}
}
Както направихме и на упражненията, разделителя се слага преди елемента (само че няма такъв преди първия елемент).
Освен това не се създава инстанция на StringBuilder
, а се взима от кеш(макар това да не е съвсем кеш в смисъла,
който може би сте срещали другаде):
StringBuilder result = StringBuilderCache.Acquire();
а след приключване на работата се освобождава:
return StringBuilderCache.GetStringAndRelease(result);
Създаването и инициализирането на обекти (особено на някой обекти, като нишки, връзка с база данни и тн) отнема време.
За това в някой случай е по-добре да се преизпълват съществуващи инстанции на класа.
За тази концепция можете да прочетете тук.
Между другото, ако искате да видите изходния код на StringBuilderCache
, просто кликнете на името - това е линк.
Обърнете внимание и че проверяват values
за null
if (values==null)
throw new ArgumentNullException("values");
Това, както и извикването на Contracts
класа, е част от концепция, която е обяснена
тук и най-вероятно ви е позната от ООП.
И стигнахме до "по-надолу". Интересно, че ако някой елемент от values
е null
, то той просто не се добавя към резултата.
С изключение на първият елемент - ако той е null се връща празен низ. Интересно каква е логиката зад това.
Аз лично не се сещам. Както се казва помагайте :)
Виждате колко интересни неща могат да се научат, ако човек порови дори в един наглед "прост" метод - обединяването на елементите на масив в символен низ.