Я могу обнаружить, дали ли мне новый объект в качестве параметра?

Короткая версия

Для тех, у кого нет времени для чтения моего обоснования для этого вопроса ниже:

Там какой-либо путь состоит в том, чтобы осуществить политику "новых объектов только" или "существующих объектов только" для параметров метода?

Долгая версия

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

var people = new List<Person>();

Person bob = new Person("Bob");

people.Add(bob);
people.Add(new Person("Larry"));

Здесь List<Person>.Add метод взял "существующее" Person (Bob), а также "новое" Person (Larry) и список содержат оба объекта. К Bob можно получить доступ как также bob или people[0]. К Larry можно получить доступ как people[1] и при желании кэшируемый и получил доступ как larry (или безотносительно) после этого.

Хорошо, прекрасный. Но иногда метод действительно не должен быть передан новый объект. Возьмите, например, Array.Sort<T>. Следующее не имеет много смысла:

Array.Sort<int>(new int[] {5, 6, 3, 7, 2, 1});

Весь вышеупомянутый код делает взятие новый массив, отсортируйте его и затем забудьте его (поскольку его подсчет ссылок достигает нуля после Array.Sort<int> выходы и сортированный массив будут поэтому собраны "мусор", если я не ошибусь). Так Array.Sort<T> ожидает "существующий" массив как его аргумент.

Существуют очевидно другие методы, которые могут ожидать "новые" объекты (хотя я обычно думал бы, что иметь такое ожидание будет ошибкой дизайна). Несовершенный пример был бы этим:

DataTable firstTable = myDataSet.Tables["FirstTable"];
DataTable secondTable = myDataSet.Tables["SecondTable"];

firstTable.Rows.Add(secondTable.Rows[0]);

Как я сказал, это не яркий пример с тех пор DataRowCollection.Add на самом деле не ожидает новое DataRow, точно; но это действительно ожидает a DataRow это уже не принадлежит a DataTable. Таким образом, последняя строка в коде выше не будет работать; это должно быть:

firstTable.ImportRow(secondTable.Rows[0]);

Так или иначе это - большая установка для моего вопроса, который является: там какой-либо путь состоит в том, чтобы осуществить политику "новых объектов только" или "существующих объектов только" для параметров метода, любого в его определении (возможно, некоторыми пользовательскими атрибутами, о которых я не знаю) или в рамках самого метода (возможно, отражением, хотя я, вероятно, уклонился бы от этого, даже если бы это было доступно)?

В противном случае любые интересные идеи относительно того, как возможно выполнить это, были бы больше, чем приветствие. Например, я предполагаю, был ли некоторый способ получить подсчет ссылок GC для данного объекта, Вы могли сказать сразу же в начале метода, получили ли Вы новый объект или не (предположение, что Вы имеете дело со ссылочными типами, конечно - который является единственным сценарием, для которого этот вопрос относится так или иначе).


Править:

Более длительная версия становится дольше.

Хорошо, предположите, что у меня есть некоторый метод, что я хочу дополнительно принять a TextWriter производить его прогресс или what-have-you:

static void TryDoSomething(TextWriter output) {
    // do something...
    if (output != null)
        output.WriteLine("Did something...");

    // do something else...
    if (output != null)
        output.WriteLine("Did something else...");

    // etc. etc.

    if (output != null)
        // do I call output.Close() or not?
}

static void TryDoSomething() {
    TryDoSomething(null);
}

Теперь, давайте рассмотрим два различных способа, которыми я мог назвать этот метод:

string path = GetFilePath();
using (StreamWriter writer = new StreamWriter(path)) {
    TryDoSomething(writer);

    // do more things with writer
}

ИЛИ:

TryDoSomething(new StreamWriter(path));

Хм... казалось бы, что это создает проблему, не так ли? Я создал a StreamWriter, который реализует IDisposable, но TryDoSomething движение не должно предполагать, чтобы знать, имеет ли оно эксклюзивный доступ к output аргумент или нет. Таким образом, объект или расположен преждевременно (в первом случае) или не становится склонным вообще (во втором случае).

Я не говорю, что это было бы большим дизайном, обязательно. Возможно, Josh Stodola прав, и это - просто плохая идея от запуска. Так или иначе я задал вопрос главным образом, потому что мне было просто любопытно, если такая вещь была возможна. Похож на ответ: не действительно.

8
задан Dan Tao 17 December 2009 в 16:22
поделиться

9 ответов

Нет, в основном.

На самом деле нет никакой разницы между:

var x = new ...;
Foo(x);

и

Foo(new ...);

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

Обратите внимание, что в примере DataRow / DataTable , есть альтернативный подход - DataRow может знать своего родителя как часть своего состояния. Это не то же самое, что быть «новым» или нет - например, у вас может быть операция «отсоединения». Определение условий в терминах подлинного жесткого состояния объекта имеет гораздо больше смысла, чем туманные термины, такие как «новый».

10
ответ дан 5 December 2019 в 05:34
поделиться

Один из способов узнать, был ли объект, переданный в функцию (или метод), был создан непосредственно перед вызовом функции / метода, - это наличие у объекта свойства, которое инициализируется метка времени, переданная системной функцией; таким образом, глядя на это свойство, можно было бы решить проблему.

Честно говоря, я бы не стал использовать такой метод, потому что

  • я не вижу причин, по которым код должен быть теперь, если переданный параметр является объектом, созданным правильно, или если он был создан в другой момент.
  • ] Предлагаемый мной метод зависит от системной функции, которая в некоторых системах отсутствовала или могла быть менее надежной.
  • С современными процессорами, которые намного быстрее, чем процессоры, используемые 10 лет назад, В этом случае проблема заключается в том, чтобы забыть добавить код для изменения этого свойства в каждом методе.

    Я снова спрашивал себя: «А действительно ли это нужно?»

0
ответ дан 5 December 2019 в 05:34
поделиться

Нет. И если есть какая-то причина, по которой вам нужно это сделать, ваш код имеет неправильную архитектуру.

5
ответ дан 5 December 2019 в 05:34
поделиться

Не говоря уже о том, что нелатинские (китайский, арабский, греческий, иврит, кириллица и т. Д.) Доменные имена будут разрешены в ближайшем будущем . Каждый должен изменить используемое регулярное выражение электронной почты, потому что эти символы, безусловно, не подпадают под ни [az] / i , ни \ w . Все они потерпят неудачу.

В конце концов, лучший способ проверить адрес электронной почты - это на самом деле отправить электронное письмо на указанный адрес для подтверждения адреса. Если адрес электронной почты является частью аутентификации пользователя (регистрация / вход / и т. Д.), То вы можете идеально совместить его с системой активации пользователя. Т.е. отправить электронное письмо со ссылкой с уникальным ключом активации на указанный адрес электронной почты и разрешить вход только в том случае, если пользователь активировал вновь созданную учетную запись, используя ссылку в электронном письме.

Если целью регулярного выражения является просто быстрое информирование пользователь в пользовательском интерфейсе указывает, что указанный адрес электронной почты не выглядит в правильном формате, лучше всего проверить, соответствует ли он в основном следующему регулярному выражению:

^([^.@]+)(\.[^.@]+)*@([^.@]+\.)+([^.@]+)$

Все просто. Зачем вам нужны символы, используемые в имени и домене? Ответственность за ввод действующего адреса электронной почты лежит на клиенте, а не на сервере. Даже когда клиент вводит синтаксически действительный адрес электронной почты, например t выглядит в правильном формате, лучше всего проверить, соответствует ли он в основном следующему регулярному выражению:

^([^.@]+)(\.[^.@]+)*@([^.@]+\.)+([^.@]+)$

Все просто. Зачем вам нужны символы, используемые в имени и домене? Ответственность за ввод действующего адреса электронной почты лежит на клиенте, а не на сервере. Даже когда клиент вводит синтаксически действительный адрес электронной почты, например t выглядит в правильном формате, лучше всего проверить, соответствует ли он в основном следующему регулярному выражению:

^([^.@]+)(\.[^.@]+)*@([^.@]+\.)+([^.@]+)$

Все просто. Зачем вам нужны символы, используемые в имени и домене? Ответственность за ввод действующего адреса электронной почты лежит на клиенте, а не на сервере. Даже когда клиент вводит синтаксически действительный адрес электронной почты, например aa@bb.cc , это не гарантирует, что это действительный адрес электронной почты. Ни одно регулярное выражение не может покрыть это.

Если вы собираетесь использовать объект для чего-либо, и нет никаких побочных эффектов, кроме изменения объекта, то этот код не должен существовать с самого начала.

Это похоже на код, который выглядит так:

1 + 2;

Что делает этот код делать? Что ж, в зависимости от компилятора C / C ++ он может компилироваться во что-то, что оценивает 1 + 2. Но что дальше, где хранится результат? Вы используете его для чего-нибудь? Нет? Тогда почему этот код является частью вашего исходного кода?

Конечно, вы можете возразить, что код на самом деле a + b; , и цель состоит в том, чтобы гарантировать, что оценка a + b не будет генерировать исключение, обозначающее переполнение, но такой случай настолько убывающе редок, что его особый случай просто замаскирует реальные проблемы,

3
ответ дан 5 December 2019 в 05:34
поделиться

Да, есть способ сделать это.

Типа.

Если вы сделаете свой параметр параметром ref , вам понадобится существующая переменная в качестве аргумента. Вы не можете сделать что-то вроде этого:

DoSomething(ref new Customer());

Если вы это сделаете, вы получите ошибку «Аргумент ref или out должен быть присваиваемой переменной».

Конечно, использование ref имеет другие последствия. Однако, если вы пишете метод, вам не нужно о них беспокоиться. Пока вы не переназначаете параметр ref внутри метода, не будет иметь никакого значения, используете вы ref или нет.

Я не говорю, что это обязательно хороший стиль. Вы не должны использовать ref или out, если вам действительно, действительно не нужно и у вас нет другого способа делать то, что вы делаете. Но использование ref заставит то, что вы хотите делать, работать.

6
ответ дан 5 December 2019 в 05:34
поделиться

Краткий ответ - нет, нет

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

// For example create a method that allows you to do this:
people.Add("Larry");

// Instead of this:
people.Add(new Person("Larry"));

// The new method might look a little like this:
public void Add(string name)
{
    Person person = new Person(name);
    this.add(person); // This method could be private if neccessary
}
3
ответ дан 5 December 2019 в 05:34
поделиться

Нет, нет никакого способа узнать.

Все, что передается, - это ссылка на объект. Независимо от того, создается ли он на месте или получен из массива, рассматриваемый метод не имеет возможности узнать, как и / или где были созданы передаваемые параметры.

0
ответ дан 5 December 2019 в 05:34
поделиться

Я могу придумать, как это сделать, но я бы определенно не рекомендовал это. Просто для аргументации.

Что значит для объекта быть "новым" объектом? Это означает, что существует только одна ссылка, поддерживающая его жизнь. «Существующий» объект будет иметь более одной ссылки на него.

Имея это в виду, посмотрите на следующий код:

    class Program
    {
        static void Main(string[] args)
        {
            object o = new object();

            Console.WriteLine(IsExistingObject(o));
            Console.WriteLine(IsExistingObject(new object()));

            o.ToString();  // Just something to simulate further usage of o.  If we didn't do this, in a release build, o would be collected by the GC.Collect call in IsExistingObject. (not in a Debug build)
        }

        public static bool IsExistingObject(object o)
        {
            var oRef = new WeakReference(o);

#if DEBUG 
            o = null; // In Debug, we need to set o to null.  This is not necessary in a release build.
#endif
            GC.Collect();
            GC.WaitForPendingFinalizers();

            return oRef.IsAlive;
        }
    }

Он печатает True в первой строке, False во второй. Но опять же, пожалуйста, не используйте это в своем коде.

3
ответ дан 5 December 2019 в 05:34
поделиться

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

0
ответ дан 5 December 2019 в 05:34
поделиться
Другие вопросы по тегам:

Похожие вопросы: