正如問題所述,我正在學習 C#,我想知道是否有可能擁有一個行為類似于 javascript 中的物件的全域可用物件?
意思是,它應該具有以下功能:
- 可以有一些具有默認值的公共屬性(最好是任何型別,但至少是字串)
- 可以匯入并在另一個模塊中全域可用
- 可以從另一個模塊訪問默認屬性(并在可能的情況下進行編輯,但不是必需的)
- 可以從另一個模塊設定新屬性
現在不幸的是,我真的不知道如何處理這項任務。我知道獲取和更新屬性需要定義“setters”和“getters”,這對于默認屬性很好,但是如何從另一個模塊動態創建新屬性?
是否可以為這個新屬性分配任意型別,而無需在 setter 中定義型別?我知道字典可以在 C# 中使用,但我仍然必須指定型別,而且我不知道如何使它們像全域類一樣運行,而不是必須將它們作為屬性傳遞,如果我想在 a 中使用我的字典不同的階級
作為一個簡化的示例,在 javascript 中,我們可以執行以下操作:
一些組件.js:
export default {
prop1: "somevalue",
prop2: null,
}
主檔案.js:
import myComponent from './somecomponent.js'
console.log(myComponent.prop1) // will output "somevalue"
myComponent.prop2 = True // will set the value of prop2 to True, from null
myComponent.newProp = {} // will create a new property that did not exist before, and assign an object to it
現在,顯然 C# 將有不同的語法,但有可能有一些功能相似的東西嗎?
uj5u.com熱心網友回復:
你能在 C# 中創建類似 JavaScript 的物件嗎
你不能。C# 和 JavaScript 是解決不同問題的不同語言,因此它們具有非常不同的型別系統和資料建模方法。
我制作了這張表,我希望它能指出 C# 和 JavaScript 型別系統之間的根本區別。特別是,我想提請您注意“屬性”是什么以及object含義方面的重要和根本差異。
| C# | JavaScript | |
|---|---|---|
| 產品型別 | class 或者 struct |
object 實體 |
| 型別擴展范式 | 單繼承(型別繼承) | 原型繼承(值繼承) |
Object foo = /object foo = 是... |
對記憶體中指定型別的 GC 堆分配的定義區域的任意參考(包括裝箱值)。 | 一個哈希表實體。 |
| 屬性是... | 靜態定義的 getter/setter 方法,帶或不帶支持欄位。 | 具有鍵和任意值的哈希表條目string(包括標量、物件(其他哈希表)和函式) |
class 是... |
完全是靜態定義的型別。 | 建構式的簡寫。 |
| 型別標識 | 靜態定義。型別標識是嚴格主格的。 | (通常)運行時定義。弱結構型別。正確使用建構式時型別標識是主格的(例如instanceof)。TypeScript 引入了嚴格的結構型別。 |
| 未定義的型別名稱 | 編譯器錯誤 | 運行時錯誤。 |
| 未定義的型別成員取消參考 | 編譯器錯誤 | 運行時錯誤。 |
我想知道是否有可能擁有一個行為類似于 javascript 中的物件的全域可用物件?
- C# 沒有全域變數。
- 在 C# 中,每個值和方法(函式)都必須存在于型別定義中(例如
class、struct、interface等)。
- 在 C# 中,每個值和方法(函式)都必須存在于型別定義中(例如
- 我想最接近的等價物是通過
public static欄位或屬性公開的值,但除非該值的型別是不可變的,否則由于執行緒安全和并發問題,您不應該從 C# 程式代碼中對其進行操作。JavaScript 沒有這個問題,因為 JavaScript 嚴格來說是單執行緒的,不允許并發訪問共享資源。
可以有一些具有默認值的公共屬性(最好是任何型別,但至少是字串)
class您可以在 C# 中使用具有默認值的屬性定義型別(即 a ),這些屬性可以被傳遞給類的建構式的顯式值覆寫。但是您仍然需要為這些屬性指定確切的型別。
如果您想要帶有命名條目的任意值集合,請改用 a Dictionary<K,V>。作為Dictionary<K,V>強型別,您需要使用V: Object(ewwwww) 或定義一個自定義聯合struct,該聯合明確且詳盡地涵蓋所有可能的類似 JavaScript 的值和 Func函式屬性的Action委托型別(如果您愿意的話)。
可以匯入并在另一個模塊中全域可用
- C# 沒有“匯入”或“模塊”。
- 在 C# 中,任何檔案中的任何
public或internal型別都.cs可以被.cs同一專案中的任何其他檔案使用。- 盡管
internal型別不能被任何其他專案使用。 - 并且您可能需要在
using YourProject.ChildNamespace使用檔案中添加一個。
- 盡管
可以從另一個模塊訪問默認屬性(并在可能的情況下進行編輯,但不是必需的)
我不確定你在這里的意思,因為“默認屬性”在 JavaScript 和 C# 中根本不是一個東西。
我知道獲取和更新屬性需要定義“setters”和“getters”,這對于默認屬性很好,但是如何從另一個模塊動態創建新屬性?
同樣,“默認”在這兩種語言的背景關系中都不是有意義的術語。
And C# is a strictly-typed, statically-typed language: you cannot add or remove members to types at runtime (at least as far as this conversation is concerned).
Remember that a JavaScript object is really just a view of a hashtable, so it sounds to me like you just need a Dictionary<K,V>, or even ExpandoObject (as @Matthew suggests) as your post implies that you don't want to take advantage of type-safety at all.
Is it possible to assign arbitrary types to this new property, without defining the type in the setter?
Not at runtime. Only statically (i.e. in a defined class which is compiled once at design-time). Note that the dynamic type in C# is really just an ExpandoObject behind-the-scenes, which is just another kind of dictionary/map/hashtable, just like JavaScript's object type.
I know that dictionaries can be used in C#, but i still have to specify the type
While you do-indeed have-to specify the TValue type when using Dictionary<TKey,TValue>, it shouldn't be a blocking problem though, as you can use any of the following types for TValue:
- Use
Object?(orObjectif you're targeting .NET Framework 4.x) forTValue, but this is not type-safe and introduces too many awkward consequences for me to seriously consider. - Using
dynamic(forTValue) has the same issues as usingObject. - Using
dynamic(instead of aDictionary<TKey,TValue>entirely) is, just, no. - Using a type-union (tip: use
OneOf<>) to define the exhaustive set of valid types. This can also be recursive.
Below is my first personal interpretation of your JavaScript example, using only statically defined and immutable types (as thread-safety is largely impossible without immutable data):
MyValues.cs:
using System;
namespace MyProject
{
public class MyValues
{
public String Prop1 { get; init; } = "somevalue";
public Boolean? Prop2 { get; init; } = null;
}
public class MyDerived : MyValues
{
public SomeOtherType NewProp { get; init; }
}
}
Program.cs:
using System;
namespace MyProject
{
public static class Program
{
public static void Main()
{
MyValues myValues1 = new MyValues()
{
Prop2 = false
};
Console.WriteLine( "Prop1: \"{0}\".", myValues1.Prop1 ); // "Prop1: "somevalue"."
Console.WriteLine( "Prop2: {0}.", myValues1.Prop2 ); // "Prop2: True."
//
MyDerived myValues2 = new MyDerived()
{
Prop1 = "newStringValue",
Prop2 = true,
NewProp = new SomeOtherType()
};
Console.WriteLine( "Prop1: \"{0}\".", myValues2.Prop1 ); // "Prop1: "newStringValue"."
Console.WriteLine( "Prop2: {0}.", myValues2.Prop2 ); // "Prop2: True."
}
}
}
Note that:
- The
Prop1andProp2properties inclass MyValuesabove wrap corresponding hidden private instance fields generated by the compiler. Prior to C# 3.0 every property declaration had to have an explicit backing field. MyDerivedinherits fromMyValues(so it gainsProp1andProp2implicitly), however this is just for demonstration purposes: in C# you should not use inheritance as a poor substitute for mixins and member-sharing. Only use inheritance when you're meaningfully implementing an "is" relationship and not a "has" relationship.- Unfortunately even C# 10.0 (released in November 2021) still doesn't have true mixins and similarly its support for object-composition is very weak (let alone any kind of true algebratic types (ADTs)). While C# 11 will likely have simpler ADTs I'm not expecting support for true unions, products, and intersection-types until after C# 12 or even 13.
- Since C# 9.0, properties can be declared with
init-setters, which indicates that the property can only be set inside a constructor or inside an object-initializer. My example is usinginit-setters for simplicity, but they should be avoided in production-code except for truly optional values and never for any required values because the constructor cannot provide guarantees about object-initializers, as object-initializers run after the constructor has returned.
Below is my second personal interpretation of your JavaScript example, using dictionary collection-types with a OneOf<...> union to add type-safety.
警告:我global using在使用 C# 10.0 時遇到了問題,尤其是在使用自參考型別別名時。雖然我下面的代碼無法編譯,但仍然可以將型別別名轉換為struct實作相同效果的包裝器。
實用程式.cs:
namespace MyProject
{
// "Global Usings" imbue a type-alias with project-wide visibility, but requires C# 10.0.
// Also, this doesn't compile for me, but *in-principle* the code below is still correct.
global using ValueDict = Dictionary<String,PossibleValueTypes>;
global using PossibleValueTypes = OneOf<ValueDict,String,Int32,Boolean,Decimal,Null>;
global using ImmutablePossibleValueTypes = OneOf<ImmutableValueDict,String,Int32,Boolean,Decimal>;
global using ImmutableValueDict = ImmutableDictionary<String,PossibleValueTypes,Null>;
// This empty struct is used as a type-tag to allow `null` (which is a reference-value in C#/.NET) to be used _almost_ like a type (just like in TypeScript, where `null` is both a type and a value).
// But as C# is not JavaScript you really shouldn't ever do this (as C# has (almost) first-class support for nullable reference types already).
public struct Null {}
}
MyValues.cs:
using System;
using OneOf;
namespace MyProject
{
public static class MyValues
{
public static ImmutableValueDict Values { get; } = new ImmutableValueDict()
{
{ "Prop1", "someValue" },
{ "Prop2", new Null() },
};
}
}
程式.cs:
using System;
namespace MyProject
{
public static class Program
{
public static void Main()
{
ImmutableValueDict myValues1 = MyValues.Values;
Console.WriteLine( "Prop1: \"{0}\".", myValues1.Prop1 ); // "Prop1: "somevalue"."
Console.WriteLine( "Prop2: {0}.", myValues1.Prop2 ); // "Prop2: MyProject.Null"
//
ImmutableValueDict myValues2 = new ImmutableValueDict( myValues1 ) // <-- This constructor overload will copy the entries in `myValues1` into `myValues2` - though as `myValues2`'s collection-initializer overwrites both Prop1 and Prop2 it's kinda moot.
{
{ "Prop1", "newStringValue" ),
{ "Prop2", true ),
{ "NewProp", new ImmutableValueDict() ) // Nested dicts!
};
Console.WriteLine( "Prop1: \"{0}\".", myValues2.Prop1 ); // "Prop1: "newStringValue"."
Console.WriteLine( "Prop2: {0}.", myValues2.Prop2 ); // "Prop2: True."
}
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/415669.html
標籤:
上一篇:如何讓圖片框不相互重疊?
