Немного другая проблема, которая вызывает очень похожие сообщения компилятора, зависит от времени жизни объекта, а не для хранения явной ссылки. Примером этого является библиотека ssh2 . При разработке чего-то большего, чем тестовый проект, возникает соблазн попытаться помещать Session
и Channel
, полученные из этого сеанса рядом друг с другом, в структуру, скрывая детали реализации от пользователя. Однако обратите внимание, что определение Channel
имеет время жизни 'sess
в аннотации типа, а Session
- нет.
вызывает аналогичные ошибки компилятора, связанные со временем жизни.
Один из способов его решения очень простым образом - объявить Session
снаружи в вызывающем, а затем для аннотации ссылки в структуре со временем жизни, подобный ответ в , этот пост Форума пользователя Rust говорит об одной и той же проблеме при инкапсуляции SFTP. Это не будет выглядеть элегантно и может не всегда применяться - потому что теперь у вас есть два объекта для работы, а не тот, который вам нужен!
Выключает арендованный ящик или owning_ref crate из другого ответа также являются решениями для этой проблемы. Рассмотрим owning_ref, который имеет специальный объект для этой цели: OwningHandle
. Чтобы избежать перемещения основного объекта, мы выделяем его в куче, используя Box
, что дает нам следующее возможное решение:
use ssh2::{Channel, Error, Session};
use std::net::TcpStream;
use owning_ref::OwningHandle;
struct DeviceSSHConnection {
tcp: TcpStream,
channel: OwningHandle, Box>>,
}
impl DeviceSSHConnection {
fn new(targ: &str, c_user: &str, c_pass: &str) -> Self {
use std::net::TcpStream;
let mut session = Session::new().unwrap();
let mut tcp = TcpStream::connect(targ).unwrap();
session.handshake(&tcp).unwrap();
session.set_timeout(5000);
session.userauth_password(c_user, c_pass).unwrap();
let mut sess = Box::new(session);
let mut oref = OwningHandle::new_with_fn(
sess,
unsafe { |x| Box::new((*x).channel_session().unwrap()) },
);
oref.shell().unwrap();
let ret = DeviceSSHConnection {
tcp: tcp,
channel: oref,
};
ret
}
}
. Результатом этого кода является то, что мы не можем использовать Session
, но он сохраняется вместе с Channel
, который мы будем использовать. Поскольку OwningHandle
вызывает раздел Box
, который разделяет Channel
, когда он хранится в структуре, мы называем его как таковой. ПРИМЕЧАНИЕ. Это только мое понимание. У меня есть подозрение, что это может быть неверно, поскольку оно, по-видимому, очень близко к обсуждению OwningHandle
небезопасности .
. Любопытная деталь здесь заключается в том, что Session
логически имеет аналогичную связь с TcpStream
, поскольку Channel
имеет значение Session
, но его собственность не принимается и нет аннотаций типа вокруг так. Вместо этого пользователь должен позаботиться об этом, поскольку в документации к методу рукопожатия говорится:
Этот сеанс не имеет права собственности на предоставленный сокет, рекомендуется, чтобы сокет сохранял время жизни этого сеанса, чтобы убедиться, что связь выполнена правильно.
Также настоятельно рекомендуется, чтобы предоставленный поток не использовался одновременно в другом месте в течение всего сеанса в качестве это может помешать протоколу.
blockquote>Таким образом, использование
TcpStream
полностью зависит от программиста, чтобы обеспечить правильность кода. СOwningHandle
внимание к тому, где происходит «опасная магия», выполняется с помощью блокаunsafe {}
.Дальнейшее и более высокоуровневое обсуждение этой проблемы находится в этом Rust User's Forum - который включает в себя другой пример и его решение с использованием ящика для аренды, который не содержит небезопасных блоков.
Примечание: Как и мы с Эриком, такой подход не является идиоматическим способом решения таких проблем. Без контекста трудно предложить альтернативные решения, но ... будьте осторожны. Отражения, как правило, лучше всего избегать, если у вас нет настоящей потребности.
Таким образом, возникает ряд проблем.
1) Назначение руководящих принципов C # CapitalCamelCase
для имен классов. Я сделаю это в этом ответе.
2) public MyPropObj prop1 = new MyPropObj(0, 1, 2);
это не свойство. Это поле, поэтому оно не подхвачено GetProperties()
. Рекомендации по C # рекомендуют против общедоступных полей, поэтому лучше всего перейти на публичные свойства:
public MyPropObj prop1 { get; set; } = new MyPropObj(0, 1, 2);
и т. Д. ...
3) Вызов GetProperties
на Type
возвращает массив из PropertyInfo
. Они, в свою очередь, выставляют метод GetValue
, который можно вызвать, чтобы получить значение свойства:
var obj = prop.GetValue(myObject); //this is always of type Object
var propObj = (MyObj.MyPropObj)obj; //so we need to cast it
Теперь мы можем взять это propObj
и
propObj.p1++;
propObj.p2++;
propObj.p3++;
как обычно. Вам не нужно отражать эту часть.
Объединяя это все:
void Main()
{
MyObj myObject = new MyObj();
Console.WriteLine(myObject.prop2.p2); //2
foreach (var prop in typeof(MyObj).GetProperties())
{
var propObj = (MyObj.MyPropObj)prop.GetValue(myObject);
propObj.p1++;
propObj.p2++;
propObj.p3++;
}
Console.WriteLine(myObject.prop2.p2); //3
}
class MyObj
{
public class MyPropObj
{
public int p1 { get; set; }
public int p2 { get; set; }
public int p3 { get; set; }
public MyPropObj(int one, int two, int three)
{
p1 = one;
p2 = two;
p3 = three;
}
}
public MyPropObj prop1 { get; set; } = new MyPropObj(0, 1, 2);
public MyPropObj prop2 { get; set; } = new MyPropObj(0, 2, 4);
public MyPropObj prop3 { get; set; } = new MyPropObj(0, 4, 8);
}
Однако это не идиоматический C #, а номер раз, когда вы можете решить атаковать проблему с (медленным) отражением, мало и далеко. Есть почти всегда лучшие способы взломать гайку.
prop.GetMethod.Invoke()
выглядит странно для меня. Почему бы неprop.GetValue()
? – itsme86 13 July 2018 в 22:54