#c# #object #parameters #reference #null
#c# #объект #параметры #ссылка #null
Вопрос:
Я пытаюсь объяснить свою ситуацию в небольшом коде:
Class A
{
private IDBTransaction trans;
private IDBConnection conn;
void Main()
{
B test = new B(trans)
[some instruction]
trans = conn.BeginTrans();
test.Save();
}
}
Class B
{
private IDBTransaction trans;
public B(IDBTransaction transaction)
{
trans = transaction;
}
void Save()
{
[some instruction that use transaction]
}
}
Когда Save() вызывается переменная B.trans присваивается значение null, потому что оно было null в момент передачи параметра. Затем A.trans изменяет свое значение, вызывая BeginTrans(), но B.trans по-прежнему остается нулевым. Как я могу решить?
Комментарии:
1. Почему бы просто не инициализировать
trans
перед созданием вашего экземпляраB
?2. Я не могу, потому что раньше было много инструкций. Я должен решить эту ситуацию, оставив экземпляр B перед инициализацией trans. Я инициализирую trans только там, где мне это нужно, я не могу инициализировать trans, оставляя его открытым все время. Это не очень хорошая практика.
3. Затем
Save
сделайте объект транзакции в качестве параметра.4.Конечно
B.trans
, все равноnull
, вы не передали ссылку на переменную, вы передалиnull
-reference и ссылка была скопирована. Таким образом, не имеет значения, что вы делаетеA.trans
, это не повлияет наB
. Поэтому вам необходимо предоставить объект транзакции,B
когда он у вас есть, и это означает, что вы каким-то образом должны передать егоB
послеconn.BeginTrans()
.5. @LasseV.Karlsen это единственное решение? Передать его, когда он будет оценен?
Ответ №1:
Вы можете удерживать транзакцию внутри отдельного объекта, например
class TransactionInfo
{
public IDbTransaction Transaction;
public IDbConnection conn;
}
а затем используйте его таким образом
class A
{
private static TransactionInfo info = new TransactionInfo();
static void Main()
{
B test = new B(info);
//[some instruction]
info.Transaction = info.conn.BeginTransaction();
test.Save();
}
}
class B
{
private TransactionInfo _info;
public B(TransactionInfo info)
{
_info = info;
}
public void Save()
{
//[some instruction that use transaction]
_info.Transaction;
}
}
ИЛИ вы можете отложить момент, когда захотите получить транзакцию
public class ValueSource<T>
{
private Func<T> _acessor;
public ValueSource(Func<T> acessor)
{
_acessor = acessor;
}
public T Value
{
get
{
try
{
return _acessor();
}
catch
{
return default(T);
}
}
}
}
А затем передайте любое из приведенных выше решений конструктору. Пример для задержки (для хранения в отдельном классе пример такой же)
class A
{
private static IDbTransaction trans;
private static IDbConnection conn;
static void Main()
{
B test = new B(new ValueSource<IDbTransaction>(()=>trans));
//[some instruction]
trans = conn.BeginTransaction();
test.Save();
}
}
class B
{
private ValueSource<IDbTransaction> trans;
public B(ValueSource<IDbTransaction> transactionSource)
{
trans = transactionSource;
}
public void Save()
{
//[some instruction that use transaction]
trans.Value;
}
}
НО.Все приведенные выше примеры применимы только в том случае, если вы по какой-либо причине хотите сохранить ссылку на транзакцию в классе B. Если в этом нет необходимости, я предлагаю (как было предложено в комментариях к вопросу) передать транзакцию в качестве Save
параметра метода.
class A
{
private static IDbTransaction Transaction;
private static IDbConnection conn;
static void Main()
{
B test = new B();
//[some instruction]
Transaction = conn.BeginTransaction();
test.Save(Transaction);
}
}
class B
{
public B()
{
}
public void Save(IDbTransaction transaction)
{
//[some instruction that use transaction]
transaction
}
}
Просто помните: не храните ничего внутри структуры данных, если это действительно не нужно. Классы без состояния содержат меньше ошибок, и их легче тестировать.
Комментарии:
1. почему вы делаете два поля класса A «статическими»?
2. И я не понимаю, почему вы говорите, что это откладывается.
3.
static
— просто для компиляции кода. Отложено только для —ValueSource<t>
потому что, если запрашивать, например, только тогда, когда этот экземпляр понадобится.4. Каждый раз, когда я вызываю свойство ‘ .Value’, он вызывает функцию, которая возвращает экземпляр. Я прав?
5. что, если я хочу сохранить информацию о транзакции в классе, другим способом, который вы укажете?
Ответ №2:
Я бы передал параметр trans
as Save()
методу вместо конструктора, бесполезно хранить ссылку на транзакцию для объекта, который имеет более длительный жизненный цикл. (по крайней мере, дольше, чем транзакция)Таким образом, вы можете использовать Save(trans)
метод для разных / нескольких транзакций.
void Main()
{
B test = new B();
[some instruction]
trans = conn.BeginTrans();
test.Save(trans);
}
Class B
{
void Save(IDBTransaction transaction)
{
}
}