Конструкторы C# с теми же подписями параметра

Немного другая проблема, которая вызывает очень похожие сообщения компилятора, зависит от времени жизни объекта, а не для хранения явной ссылки. Примером этого является библиотека 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 - который включает в себя другой пример и его решение с использованием ящика для аренды, который не содержит небезопасных блоков.

26
задан Doogal 28 January 2009 в 15:36
поделиться

7 ответов

Вы можете, использовал именованную идиому конструктора:

public class Thing
{
    private string connectionString;

    private string filename;

    private Thing()
    {
        /* Make this private to clear things up */
    }

    public static Thing WithConnection(string connectionString)
    {
        var thing = new Thing();
        thing.connectionString = connectionString;
        return thing;
    }

    public static Thing WithFilename(string filename)
    {
        var thing = new Thing();
        thing.filename = filename;
        return thing;
    }
}
42
ответ дан Matteo Migliore 15 October 2019 в 07:40
поделиться

Ну, существует несколько потенциалов - что считало изящным, зависит от сценария использования.

  • Статические методы фабрики, тот вызов в частного конструктора.

    static Thing thingWithFileName(string fileName)
    
  • Создают другой тип для одного из параметров или используют встроенное. Вместо строкового имени файла, Вы могли использовать Систему. IO.FileStream. Это также более безопасно с точки зрения типов, поскольку я не могу случайно передать неправильные данные в неправильный статический метод или поле.

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

    enum ThingType { FileName, ConnectionString }
    Thing(string str, ThingType type) ...
    
  • Вещь Подкласса, таким образом, у Вас есть ConnectionTypeThing и FileBackedThing

  • Полностью, устраняет Вещь, делающую, это - соединение и предварительно соединило обеспеченные источники данных. Таким образом, Вы заканчиваете с [1 113]

    Thing(InputStream dataSource)
    

    или что-то аналогичное.

Мои деньги "на элегантность" идут или на первые или вторые предложения, но мне было бы нужно больше контекста, чтобы быть довольным любым выбором.

7
ответ дан Adam Wright 15 October 2019 в 07:40
поделиться

Можно сделать всех конструкторов частными и создать методы фабрики (статические методы для класса как CreateFromConnectionString ()).

5
ответ дан Brian 15 October 2019 в 07:40
поделиться

Они на самом деле походят на различные "вещи" мне, или класс, связанный с файлом или класс, связанный с базой данных. Я определил бы интерфейс, затем имел бы отдельные реализации для каждого. Используйте Фабрику для генерации корректной реализации.

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

public interface IThing
{
   ... methods to do the things that Things do
}

public class FileThing : IThing
{
  ... file-based methods
}

public class DatabaseThing : IThing
{
  ... database-based methods
}

public static class ThingFactory
{
     public IThing GetFileThing( string name )
     {
         return new FileThing( name );
     }

     public IThing GetDatabaseThing( string connectionString )
     {
         return new DatabaseThing( connectionString );
     }
}

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

5
ответ дан tvanfosson 15 October 2019 в 07:40
поделиться

Сделайте две общественных собственности ConnectionString и FileName и затем используйте их для заполнения объекта.

В C# можно использовать объект initalizer. Как это:

Thing thing = new Thing{FileName = "abc", ConnectionString = "123"};
1
ответ дан Gerrie Schenck 15 October 2019 в 07:40
поделиться

Вот некоторые обходные решения.

Имеют одного конструктора, который берет строку подключения, и затем имейте метод фабрики для класса, который берет имя файла. Что-то вроде этого:

public static Thing CreateThing(string fileName)

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

Другая опция, должен иметь перечисление, которое имеет два типа в ней. FileName и ConnectionString. Затем просто имейте одного конструктора, который берет строку и перечисление. Затем на основе перечисления можно определить который способ пойти.

0
ответ дан BFree 15 October 2019 в 07:40
поделиться

Мне нравятся статические функции конструктора:

class Thing
{
   public static Thing NewConnection(string connectionString)
   {
       return new Thing(connectionString, true);
   }

   public static Thing NewFile(string fileName);
   {
        return new Thing(fileName, false);
   }
}
.
.
.
{
    var myObj = Thing.NewConnection("connect=foo");
    var Obj2 = Thing.NewFile("myFile.txt");
}

(не показанный, но простой, реализация Конструктора Вещи с дополнительным булевым параметром).

0
ответ дан abelenky 15 October 2019 в 07:40
поделиться