In C# when creating an object we can also (at the same time) fill out its properties. Is this possible in VB.NET?
For example:
MyObject obj = new MyObject
{
Name = "Bill",
Age = 50
};
Yes, it's possible:
Dim obj As New MyObject With { .Name = "Bill", .Age = 50 }
Two important things:
Use With keyword after class name and before { ... }
Property names have to be prefixed with a dot, so you have to use .Name instead of Name
For collection initializers use From keyword:
Dim obj as New List(Of String) From { "String1", "String2" }
Related
I created empty ArrayList of class objects and one class object. I added this class object with some parameters into ArrayList and then changed some parameters of this object. But class object that left in ArrayList also changed. That looks like I'm passing parameters by reference. Also, if I'm trying to create new object and make it equal with "=", that object becoming connected to old one, so if I'm changing first object then second one changing as well like reference to first variable. I've ran into same problem in Java (no surprise). So, how can I pass class object by value? I want to use one class object to add new objects into ArrayList and then change it without changing class object inside ArrayList.
class TestClass(var someData : Int = 0)
fun main(){
var testArrayList = arrayListOf<TestClass>()
var classObject : TestClass = TestClass(3)
testArrayList.add(classObject)
classObject.someData = 5
testArrayList.add(classObject)
classObject.someData = 7
testArrayList.add(classObject)
print(testArrayList[0].someData)
print(testArrayList[1].someData)
print(testArrayList[2].someData) //Output = 777. Needed 357
}
Same with creating new objects
class TestClass(var someData : Int = 0)
fun main(){
var testArrayList = arrayListOf<TestClass>()
var classObject : TestClass = TestClass(3)
var secondClassObject : TestClass = classObject
testArrayList.add(secondClassObject)
classObject.someData = 5
var thirdClassObject : TestClass = classObject
testArrayList.add(thirdClassObject)
classObject.someData = 7
var fourthClassObject : TestClass = classObject
testArrayList.add(classObject)
print(testArrayList[0].someData)
print(testArrayList[1].someData)
print(testArrayList[2].someData) //Output = 777. Needed 357
}
You try to make similar code in Java because arrayList in Java works the same in this situation. So, you can send answer in Java code
You are basically adding the same instance of TestClass over and again with modified value, so the instances in array position 3 will also point to same instance of 1, if you really want different values, you need to either create a different instances, or use data class with copy which copies the values into different instance and you can named parameters to change values you need as below
data class TestClass(var someData : Int = 0)
fun main(){
var testArrayList = arrayListOf<TestClass>()
var classObject : TestClass = TestClass(3)
testArrayList.add(classObject)
testArrayList.add(classObject.copy(someData = 5))
testArrayList.add(classObject.copy(someData = 7))
print(testArrayList[0].someData)
print(testArrayList[1].someData)
print(testArrayList[2].someData)
}
copy
To copy an object for changing some of its properties, but keeping the
rest unchanged, use the copy() function.
class TestClass(var someData : Int = 0) {
fun copy(someData: Int = this.someData) = TestClass(someData)
}
fun main(){
var testArrayList = arrayListOf<TestClass>()
var classObject : TestClass = TestClass(3)
testArrayList.add(classObject)
testArrayList.add(classObject.copy(someData = 5))
testArrayList.add(classObject.copy(someData = 7))
print(testArrayList[0].someData)
print(testArrayList[1].someData)
print(testArrayList[2].someData)
}
I want to add new properties in JSON object.
I did the following to get my result.
In MapJson.vb Class
Public Class MapJson
Public Class item
Public Property ColList As ColMap
End Class
Public Class ColMap
Public Property inv As String
End Class
End Class`
In a winform button click:
Dim ColLst As New List(Of MapJson.ColMap)
Dim ColItem As New MapJson.ColMap With {
.inv = "ABC"
}
ColLst.Add(ColItem)
Dim YearItem As New MapJson.item With {
.ColList = ColLst.Single
}
Dim CreateNewJson As String = JsonConvert.SerializeObject(YearItem, Formatting.Indented)
MsgBox(CreateNewJson)
The result I get is:
{
"ColList": {
"inv": "ABC"
}
}
Now I want to add properties to ColList Object.
Properties can be anything so I can't predefine it in the class.
I want something Like this :
{
"ColList": {
"inv": "ABC",
"anyname1": "anyStringValue1",
"anyname2": "anyStringValue1",
"anyname3": "anyStringValue1"
}
}
I searched internet and found this.
Dim jo As JObject = JObject.FromObject(jsontext)
jo.Add("feeClass", "A")
jo.Add("feeClass1", "B")
jo.Add("feeClass2", "C")
jo.Add("feeClass3", "D")
MsgBox(jo.ToString)
This works properly but I don't know how to add it to the ColList Object.
Thank you for you help in advance. :)
If you know the property names ahead of time, you could just create an anonymous type to serialize:
Dim YearItem = New With {
.ColList = New With {
.inv = "ABC"
.anyname1 = "anyStringValue1"
.anyname2 = "anyStringValue1"
.anyname3 = "anyStringValue1"
}
}
Crucially, the first line of the code doesn't state MapJson.item etc, it just says New With {
You don't really need your classes with this, if all youre doing is creating them for serialization purposes, it's probably neater to just create an anonymous one
If you don't know them ahead of time, replace the ColMap with a dictionary:
Dim x = New With {
.ColList = New Dictionary(Of String, String)
}
x.ColList.Add("inv", "ABC")
x.ColList.Add("anyname1", "anyStringValue1")
x.ColList.Add("anyname2", "anyStringValue1")
x.ColList.Add("anyname3", "anyStringValue1")
Though this example doesn't demo that (clearly I do know them ahead of time because it wrote them into the code).. perhaps a loop that adds a list will be used in production
PS if youre desperate to keep those classes:
Public Class MapJson
Public Class item
Public Property ColList As Dictionary(Of String, String)
End Class
End Class`
Okay, I found it myself.
Posting here as there is less question related to this out there.
What I did is:
Suppose jsontext is what you got result at first, i.e
Say,
jsontext = "{
"ColList": {
"inv": "ABC"
}
}"
Later I did like this,
Dim NewJO As JObject = JObject.Parse(jsontext)
Dim NewSubJO As JObject = TryCast(NewJO("ColList"), JObject)
NewSubJO.Add("AA", "A")
NewSubJO.Add("BA", "B")
NewSubJO.Add("CA", "C")
MsgBox(NewJO.ToString)
And I got desired answer as
{
"ColList": {
"inv": "ABC",
"AA": "A",
"BA": "B",
"CA": "C"
}
}
I hope this helps to others :)
I have a path for an object within an object within an object and I want to set it using Groovy's dynamic abilities. Usually you can do so just by doing the following:
class Foo {
String bar
}
Foo foo = new Foo
foo."bar" = 'foobar'
That works OK. But what if you have nested objects? Something like:
class Foo {
Bar bar
}
class Bar {
String setMe
}
Now I want to use the dynamic setting, but
Foo foo = new Foo()
foo."bar.setMe" = 'This is the string I set into Bar'
Returns a MissingFieldException.
Any hints?
UPDATE: Thanks to Tim for pointing me in the right direction, the initial code on there works great at retrieving a property, but I need to set the value using the path string.
Here's what I came up with from the page Tim suggested:
def getProperty(object, String propertyPath) {
propertyPath.tokenize('.').inject object, {obj, prop ->
obj[prop]
}
}
void setProperty(Object object, String propertyPath, Object value) {
def pathElements = propertyPath.tokenize('.')
Object parent = getProperty(object, pathElements[0..-2].join('.'))
parent[pathElements[-1]] = value
}
Following works correctly.
foo."bar"."setMe" = 'This is the string I set into Bar';
Without getProperty overriding you can achieve the same result using "${}" syntax for GString as the below code demonstrates
class Baz {
String something
}
class Bar {
Baz baz
}
class Foo {
Bar bar
}
def foo = new Foo()
foo.bar = new Bar()
foo.bar.baz = new Baz()
def target = foo
def path = ["bar", "baz"]
for (value in path) {
target = target."${value}"
}
target."something" = "someValue"
println foo.bar.baz.something
final println prints "someValue" as expected
I'm building a site as a Single Page Application using ASP.NET MVC 4 Beta .
The sample app talks about adding new entities and it uses a constructor function for it's product entity.
However I have many entity types and I'm not going to write a constructor function for each one. This is how I am creating a new entity (name is the name of the datasource and dataTarget.upshot.upshotData is the list of entities I get back from the GetEntities method
in coffeeScript...
newItem = {}
for field, def of upshot.metadata(upshot.dataSources[name]._entityType).fields
do (field, def) ->
if def.array
newItem[field] = new ko.observableArray()
else
newItem[field] = new ko.observable()
upshot.addEntityProperties newItem, upshot.dataSources[name]._entityType
dataTarget.upshot.upshotData.push newItem
my question is if this is the best way to do it or am I missing something? I'm surprised that upshot does not seem to have a createEntity method.
in javascript...
newItem = {};
_ref = upshot.metadata(upshot.dataSources[name]._entityType).fields;
_fn = function(field, def) {
if (def.array) {
return newItem[field] = new ko.observableArray();
} else {
return newItem[field] = new ko.observable();
}
};
for (field in _ref) {
def = _ref[field];
_fn(field, def);
}
upshot.addEntityProperties(newItem, upshot.dataSources[name]._entityType);
dataTarget.upshot.upshotData.push(newItem);
var newThing = {};
var typeName = "MyType:#MyNamespace";
upshot.map({ SomeProperty: "my value" }, typeName, newThing);
upshot.addEntityProperties(newThing, typeName);
This will create your object with the entity properties mapped to observables, and will allow you to set properties (see SomeProperty:"my value").
Code below is working well as long as I have class ClassSameAssembly in same assembly as class Program.
But when I move class ClassSameAssembly to a separate assembly, a RuntimeBinderException (see below) is thrown.
Is it possible to resolve it?
using System;
namespace ConsoleApplication2
{
public static class ClassSameAssembly
{
public static dynamic GetValues()
{
return new
{
Name = "Michael", Age = 20
};
}
}
internal class Program
{
private static void Main(string[] args)
{
var d = ClassSameAssembly.GetValues();
Console.WriteLine("{0} is {1} years old", d.Name, d.Age);
}
}
}
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'object' does not contain a definition for 'Name'
at CallSite.Target(Closure , CallSite , Object )
at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
at ConsoleApplication2.Program.Main(String[] args) in C:\temp\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs:line 23
I believe the problem is that the anonymous type is generated as internal, so the binder doesn't really "know" about it as such.
Try using ExpandoObject instead:
public static dynamic GetValues()
{
dynamic expando = new ExpandoObject();
expando.Name = "Michael";
expando.Age = 20;
return expando;
}
I know that's somewhat ugly, but it's the best I can think of at the moment... I don't think you can even use an object initializer with it, because while it's strongly typed as ExpandoObject the compiler won't know what to do with "Name" and "Age". You may be able to do this:
dynamic expando = new ExpandoObject()
{
{ "Name", "Michael" },
{ "Age", 20 }
};
return expando;
but that's not much better...
You could potentially write an extension method to convert an anonymous type to an expando with the same contents via reflection. Then you could write:
return new { Name = "Michael", Age = 20 }.ToExpando();
That's pretty horrible though :(
You could use [assembly: InternalsVisibleTo("YourAssemblyName")] to make you assembly internals visible.
I ran into a similair problem and would like to add to Jon Skeets answer that there is another option. The reason I found out was that I realized that many extension methods in Asp MVC3 uses anonymous classes as input to provide html attributes (new {alt="Image alt", style="padding-top: 5px"} =>
Anyway - those functions use the constructor of the RouteValueDictionary class. I tried that myself, and sure enough it works - though only the first level (I used a multi-level structure). SO - in code this would be:
object o = new {
name = "theName",
props = new {
p1 = "prop1",
p2 = "prop2"
}
}
SeparateAssembly.TextFunc(o)
//In SeparateAssembly:
public void TextFunc(Object o) {
var rvd = new RouteValueDictionary(o);
//Does not work:
Console.WriteLine(o.name);
Console.WriteLine(o.props.p1);
//DOES work!
Console.WriteLine(rvd["name"]);
//Does not work
Console.WriteLine(rvd["props"].p1);
Console.WriteLine(rvd["props"]["p1"]);
SO... What is really going on here? A peek inside the RouteValueDictionary reveals this code (values ~= o above):
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(values))
object obj2 = descriptor.GetValue(values);
//"this.Add" would of course need to be adapted
this.Add(descriptor.Name, obj2);
}
SO - using TypeDescriptor.GetProperties(o) we would be able to get the properties and values despite the anonymous type being constructed as internal in a separate assembly! And of course this would be quite easy to extend to make it recursive. And to make an extension method if you wanted.
Hope this helps!
/Victor
Here is a rudimentary version of an extension method for ToExpandoObject that I'm sure has room for polishing.
public static ExpandoObject ToExpandoObject(this object value)
{
// Throw is a helper in my project, replace with your own check(s)
Throw<ArgumentNullException>.If(value, Predicates.IsNull, "value");
var obj = new ExpandoObject() as IDictionary<string, object>;
foreach (var property in value.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
obj.Add(property.Name, property.GetValue(value, null));
}
return obj as ExpandoObject;
}
[TestCase(1, "str", 10.75, 9.000989, true)]
public void ToExpandoObjectTests(int int1, string str1, decimal dec1, double dbl1, bool bl1)
{
DateTime now = DateTime.Now;
dynamic value = new {Int = int1, String = str1, Decimal = dec1, Double = dbl1, Bool = bl1, Now = now}.ToExpandoObject();
Assert.AreEqual(int1, value.Int);
Assert.AreEqual(str1, value.String);
Assert.AreEqual(dec1, value.Decimal);
Assert.AreEqual(dbl1, value.Double);
Assert.AreEqual(bl1, value.Bool);
Assert.AreEqual(now, value.Now);
}
Below solution worked for me in my console application projects
Put this [assembly: InternalsVisibleTo("YourAssemblyName")]
in \Properties\AssemblyInfo.cs of the separate project with function returning dynamic object.
"YourAssemblyName" is the assembly name of calling project. You can get that through Assembly.GetExecutingAssembly().FullName by executing it in calling project.
A cleaner solution would be:
var d = ClassSameAssembly.GetValues().ToDynamic();
Which is now an ExpandoObject.
Remember to reference:
Microsoft.CSharp.dll
ToExpando extension method (mentioned in Jon's answer) for the brave ones
public static class ExtensionMethods
{
public static ExpandoObject ToExpando(this object obj)
{
IDictionary<string, object> expando = new ExpandoObject();
foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(obj))
{
var value = propertyDescriptor.GetValue(obj);
expando.Add(propertyDescriptor.Name, value == null || new[]
{
typeof (Enum),
typeof (String),
typeof (Char),
typeof (Guid),
typeof (Boolean),
typeof (Byte),
typeof (Int16),
typeof (Int32),
typeof (Int64),
typeof (Single),
typeof (Double),
typeof (Decimal),
typeof (SByte),
typeof (UInt16),
typeof (UInt32),
typeof (UInt64),
typeof (DateTime),
typeof (DateTimeOffset),
typeof (TimeSpan),
}.Any(oo => oo.IsInstanceOfType(value))
? value
: value.ToExpando());
}
return (ExpandoObject)expando;
}
}
If you're already using Newtonsoft.Json in your project (or you're willing to add it for this purpose), you could implement that horrible extension method Jon Skeet is referring to in his answer like this:
public static class ObjectExtensions
{
public static ExpandoObject ToExpando(this object obj)
=> JsonConvert.DeserializeObject<ExpandoObject>(JsonConvert.SerializeObject(obj));
}