Pertanyaan C # mimic menimpa operator penugasan (=)


Saya punya sedikit masalah dengan kelas pembungkus yang sederhana yang saya miliki.

Terlihat seperti ini:

public class Wrapper<T>
{
  private T _value;

  public Wrapper<T>(T value)
  {
    _value = value;
  }

  public static implicit operator Wrapper<T>(T value)
  {
    return new Wrapper<T>(value);
  }

  public static implicit operator T(Wrapper<T> value)
  {
    return value._value;
  }
}

Saya telah mengganti konverter implisit dari dan ke T, sehingga ia berperilaku hampir seperti instance T itu sendiri.

misalnya

Wrapper<int> foo = 42;

Namun saya punya sedikit masalah ketika menugaskan satu contoh Wrapper ke yang lain, karena saya hanya ingin menetapkan nilai dari kelas Wrapper kedua.

Jadi sekarang, saya harus melakukan ini:

Wrapper<int> foo = 42;
Wrapper<int> bar = (int)foo;

Atau mengekspos _value secara publik melalui properti.

Namun karena ini ada di perpustakaan, dan saya tidak ingin pengguna untuk bergantung pada mengingat ini, apakah kalian tahu bagaimana saya bisa meniru penugasan operator penugasan?

Masalahnya hanya dengan mengubah pointer (seperti ketika menugaskan instance kelas ke yang lain), adalah bahwa saya punya kamus pointer ke objek wrapper ini, jadi saya tidak bisa mengubahnya sepanjang waktu, karena kamus akan berhenti kemudian mencocokkan.

Saya dapat melihat apakah ini agak membingungkan, jadi jika saya meninggalkan sesuatu yang penting, jangan ragu untuk bertanya :-)


5
2018-03-13 22:34


asal


Jawaban:


Karena operator penugasan tidak bisa kelebihan beban, tidak ada solusi yang benar-benar bagus. Seperti yang dikatakan orang lain, menggunakan struct akan memberi Anda semantik tugas yang Anda inginkan, tetapi kemudian Anda dihadapkan dengan semantik nilai - sering kali bukan hal yang baik.

Salah satu pilihan adalah membebani konstruktor:

public Wrapper(Wrapper<T> w)
{
    _value = w._value;
}

Yang akan menghasilkan sintaks ini:

Wrapper<int> foo = 42;
Wrapper<int> bar = new Wrapper<int>(foo);

Meskipun lebih verbose dari apa yang Anda miliki, bacaannya lebih baik.

Atau Anda bisa menambahkan Clone metode (bukan ICloneable antarmuka), sehingga Anda bisa menulis:

Wrapper<int> bar = foo.Clone();

Anda bisa benar-benar kreatif dan membebani beberapa operator, membuatnya tidak melakukan apa-apa. Saya tidak akan merekomendasikan hal itu. Menggunakan operator overloading untuk hal-hal semacam itu biasanya membuat kode menjadi samar dan sering terputus.


5
2018-03-14 05:45



Anda bisa membuat Wrapper <T> a struct. Namun saya tidak yakin apakah ini akan sesuai dengan desain aplikasi Anda atau tidak.


3
2018-03-14 02:46



Jika Anda melihat Nullable <T> ... yang melakukan hal yang sangat mirip dengan apa yang Anda lakukan di sini, ini memperlihatkan nilai internal menggunakan properti .Value.

Masalahnya hanya dengan mengubah pointer (seperti ketika menugaskan instance kelas ke yang lain), adalah bahwa saya punya kamus pointer ke objek wrapper ini, jadi saya tidak bisa mengubahnya sepanjang waktu, karena kamus akan berhenti kemudian mencocokkan.

Saya tidak yakin saya mengikuti ini, apa sebenarnya yang Anda simpan di kamus? Karena jika Anda menyimpan referensi, CLR akan memperbaruinya seperlunya.


1
2018-03-13 22:37



Ini adalah sifat untuk apa. Mereka memungkinkan Anda untuk menentukan apa artinya tugas. Anda tidak dapat mendefinisikannya untuk kelas atau struct sendiri karena mereka sudah didefinisikan oleh bahasa untuk melakukan hal-hal yang diperlukan. Cukup tambahkan a Value properti ke kelas.

Sebagai alternatif, edit pertanyaan Anda untuk memberikan deskripsi yang lebih luas tentang desain Anda dan bagaimana Wrapper ini cocok dengannya, karena seseorang mungkin dapat menyarankan pendekatan yang lebih sederhana.


0
2018-03-14 10:15



Saya hanya melihat ke dalamnya, membuat kelas struct adalah benar-benar bukan pilihan, karena memiliki beberapa logika dalam konstruktor parameterless, ditambah mewarisi kelas abstrak, yang berisi fungsi abstrak internal.

Saya tidak dapat menggunakan antarmuka, karena itu akan membuat fungsi-fungsi itu menjadi publik, yang akan mematahkan logika sepenuhnya.

Saya dapat memposting seluruh kelas jika itu akan membantu, tetapi agak panjang (130 baris) Atau saya bisa menggunakan server terpisah, jika itu lebih baik? (meskipun itu menyakitkan integritas pertanyaan ini, karena saya dapat menghapusnya akhirnya dari server itu)

Juga menjelaskan kelas sangat sulit, tanpa menulis esai lengkap: - /

Pokoknya saya akan mencoba untuk mengilustrasikan masalah yang saya hadapi.

Asumsikan 2 kelas tabel: CustomerTable dan UserTable:

public class CustomerTable
{
  Wrapper<string> Name;
}

public class UserTable
{
  Wrapper<string> Name;
}

Sekarang masalahnya adalah beberapa pengembang lain, dapat menggunakan kode di atas sebagai berikut:

CustomerTable customer = new CustomerTable();
UserTable user = new UserTable();
user.Name = customer.Name; // This breaks my internal dictionary

Apa yang harus dilakukan oleh pengembang, agar dapat berfungsi adalah:

user.Name = (string)customer.Name;

Masalahnya adalah, siapa yang waras mereka akan berpikir tentang itu, ketika menulis kode?

Bahkan jika saya menggunakan properti Value, pengembang masih harus ingat untuk menulis

user.Name = customer.Name.Value; // or user.Name.Value = ....

Dan lagi-lagi pengembang mungkin melupakan ini, dan tiba-tiba dia mendapat pengecualian, atau lebih buruk: data yang tidak bertahan ke database.

Jadi masalah saya benar-benar, bahwa saya ingin bungkus benar-benar transparan (itu harus dapat digunakan seolah-olah itu sebenarnya kelas / primitif itu membungkus). Namun ketika menetapkan dari satu bungkus ke yang lain, logika internal saya rusak.

Cukup banyak menulis, dan banyak kode - beri tahu saya jika saya berlebihan dalam menulis.


0
2018-03-14 12:30



Jangan secara implisit memberikan pembungkus Anda dengan dua cara.

public class DBValue<T>
{
    public static implicit operator DBValue <T>(T value)
    {
         return new DBValue<T>(value);
    }

    public static explicit operator T(DBValue <T> dbValue)
    {
         return dbValue.Value;
    }

    private readonly T _value;
    public T Value { get { this._value; } }

    public DBValue(T value)
    {
         this._value = value;
    }
}

Mentransmisikan dari DBValue<T> untuk T adalah konversi lossy (minimal, Anda kehilangan fakta bahwa itu adalah nilai dari database), dan dengan praktik terbaik harus eksplisit. Jika Anda tidak kehilangan apa pun dengan melakukan casting DBValue<T> untuk T, Anda mungkin juga hanya menggunakan properti yang kembali T.

Pada dasarnya, Anda telah melihat mengapa Anda tidak harus mencoba melakukan ini: jika DBValue dapat digantikan untuk T dan sebaliknya, bagaimana kompilator (atau pengembang) tahu mana yang harus dipilih?

Membutuhkan pengembang hilir untuk menulis:

string value = MyProperty.Value

atau

string value = (string)MyProperty

dari pada

string value = MyProperty

... tidak semua yang memberatkan, dan memastikan bahwa semua orang tahu persis apa yang terjadi.

EDIT:

Untuk benar-benar menjawab pertanyaan, Anda tidak dapat mengesampingkan tugas referensi - atau membuatnya seperti yang Anda miliki - tetapi Anda tidak perlu benar-benar melakukannya.


0
2018-03-14 13:19



A J Lane Saya melihat apa yang Anda maksud, dan saya rasa Anda benar - saya hanya ingin membuatnya sesederhana mungkin untuk menggunakan perpustakaan.

Alasan untuk cast implisit dari DbValue ke T, adalah hanya fungsi yang mengharapkan T.

sebagai contoh

literalSomething.Text = Server.HtmlEncode(SomeTable.SomeStringColumn);

daripada

literalSomething.Text = Server.HtmlEncode((string)SomeTable.SomeStringColumn);

Ini membutuhkan para pemain untuk menjadi implisit.

Itu dikatakan saya baru saja membaca komentar Anda saat mengetik ini, dan saya bisa melihat itu cukup masalah.

Saya pikir saya akan kembali untuk mengekspos nilai melalui properti, itu hanya membutuhkan pengembang untuk mengetik lebih banyak, dan agak membuat kode jelek saya pikir.

Bayangkan saja DbValue:

if (someValue.Value.HasValue) // someValue is DbValue<int?>

Tetapi sekali lagi itu mungkin lebih baik dengan kode "jelek", daripada kode yang berperilaku berbeda dari apa yang Anda harapkan dengan hanya membacanya.

Saya kira pertanyaan ini berakhir sebagai pertanyaan "praktik terbaik".

Jadi untuk menyimpulkan, saya akan membuat properti Nilai dan menggunakan itu bukan gips implisit, dan pengembang menggunakan perpustakaan hanya harus hidup dengan itu.

Terima kasih atas masukan Anda :-)


0
2018-03-14 14:09



Posting lama ini masih membutuhkan informasi tambahan untuk diselesaikan. Terlihat jelas bahwa perilaku asli yang diinginkan tidak dapat dicapai karena operator = tidak dapat diisi berlebih, dan demikian juga C # tidak dapat "ditipu" untuk mentransmisikan objek ke jenisnya sendiri ... itu akan selalu mendidih ke tugas referensi kelas. Tapi tulisan Steffen lebih lanjut menunjukkan kelas Wrapper yang digunakan tidak hanya dengan variabel lokal, tetapi sebagai jenis bidang kelas. Semantik yang diinginkan dapat digunakan DAN integritas kamus internal dikelola dengan menggunakan properti kelas, bukan bidang publik.


Bahkan menjaga yang asli diberikan Wrapper<T> kelas dengan kedua operator implisit, inilah kode yang akan bekerja:

public class CustomerTable
{
    private Wrapper<string> _Name;
    public Wrapper<string> Name {
        get { return _Name; }
        set { _Name = (string)value; }
    }
}

public class UserTable
{
    private Wrapper<string> _Name;
    public Wrapper<string> Name {
        get { return _Name; }
        set { _Name = (string)value; }
    }
}

Jika perubahan ini dilakukan, itu tidak akan merusak kode yang ada karena masih memungkinkan berbagai mode pengaturan properti:

CustomerTable customer = new CustomerTable();
UserTable user = new UserTable();

user.Name = customer.Name; //*** No longer breaks internal data structures

user.Name = "string literal";  // Works as expected with implicit cast operator
user.Name = (string)customer.Name; // Still allowed with explicit/implicit cast operator
user.Name = customer.Name.Value; // Also works if Value property is still defined

Karena ini masih tidak menjawab pertanyaan asli, penggunaan kelas Wrapper masih bisa bermasalah jika digunakan di luar konteks properti kelas, yaitu melewati antara objek, dll. Mungkin seluruh kelas Wrapper dapat dihilangkan dengan desain kelas yang tepat, termasuk penggunaan properti set / get accessors.


0
2017-09-09 18:44