hi I need dynamic array in attribute in c#
for example:
public class MyHtmlAttributesAttribute : System.Attribute
{
private IDictionary<string, string> Attrib { get; set; }
public MyHtmlAttributesAttribute()
{
Attrib = new Dictionary<string, string>();
}
public MyHtmlAttributesAttribute(params string[][] values)
: this()
{
foreach (var item in values)
{
if (item.Count() < 1)
throw new Exception("bad length array");
this.Attrib.Add(item[0].ToLower(), item[1]);
}
}
}
but when I want use this attribute I get error :
Array initializers can only be used in a variable or field initializer. Try using a new expression instead
I use this attribute by this style:
public class LoginViewModel
{
[Required]
[MyHtmlAttributes(new string[][]{{"Class", "ltr"}, {"AutoCompleteType" , "Disabled"}})]
public string Email { get; set; }
...
..
}
thank you for answer
The problem is not in the attribute in this case, but in the array initialization. {{"Class", "ltr"}, {"AutoCompleteType" , "Disabled"}} could be used in an array initializer, but not in a new[] expression.
With new expression: new string[][] {new string[] { "Class", "ltr" }, new string[]{ "AutoCompleteType", "Disabled" } };
But since params is used, the encapsulating new string[] can be omitted:
[MyHtmlAttributes(new string[]{"Class", "ltr"}, new string[]{"AutoCompleteType" , "Disabled"})]
The following is purely an alternative
An alternative is to allow multiple instances of the attribute to be applied, and combine them when obtaining them.
To allow multiple attributes:
[AttributeUsage(AttributeTargets.Property, AllowMultiple=true)]
public class MyHtmlAttributesAttribute : System.Attribute
Apply them as
[MyHtmlAttributes("Class", "ltr")]
[MyHtmlAttributes("AutoCompleteType", "Disabled")]
public string Email { get; set; }
Of course the constructor and implementation of the attribute has to be altered to allow only a single attr-value pair, but adding/removing a pair should only be easier this way. Combining them by reading all the MyHtmlAttributes instances on a property should be straight forward enough.
Related
I am trying to implement an Attribute class in which I also need to pass the Dictionary<string, object> in it.
When trying to do the same it is throwing an error
ABC.cs(8366,6): error CS0181: Attribute constructor parameter 'Settings' has type 'Dictionary<string, object>', which is not a valid attribute parameter type.
[AttributeUsage(AttributeTargets.All)]
public class TestAttribute: Attribute
{
public string cName{ get; private set; }
public string oName { get; private set; }
public string aUser { get; private set; }
public Dictionary<string, object> Settings { get; private set; }
public TestAttribute(string cName, string oName, string aUser = null
Dictionary<string, object> Settings = null)
{
this.cName= cName;
this.oName= oName;
this.aUser = aUser ;
this.Settings = Settings ;
}
}
I did some searching and found that attribute classes use primitive data types.
So, I wanted to know is there any way to add the Dictionary to the above class?
From the docs, you can only use certain types in an attribute:
Change the data type of the parameter to Byte, Short, Integer, Long, Single, Double, Char, String, Boolean, System.Type, or an enumeration type.
So no, you cannot use a Dictionary at all.
Having a bit more of a think about this. Since you called the property "Settings", that suggests the value should come from a config file anyway, so perhaps that should be taken into account. Another alternative would be to use an array of strings as key/value pairs and parse them out. For example:
public TestAttribute(string cName, string oName, string aUser = null,
params string[] Settings) // use params to make it easier to specify values
{
//snip
// This is just an example and should include error checking etc.
this.Settings = Settings
.Select(s => s.Split('='))
.ToDictionary(s => s[0], s=> s[1]);
}
And now use it like this:
[Test("cname", "oname", "auser", "Setting1=Foo", "Setting2=Bar")]
public class Foo
{
}
I am facing an issue, surely due to my lack of knowledge in the reflection process, while trying to set a "complex" class hierarchy based on Json files.
Here are my main model :
public class Names
{
public Weapons Weapons { get; set; }
public Armors Armors { get; set; }
public Utilities Utilities { get; set; }
public Names()
{
Weapons = new Weapons();
Armors = new Armors();
Utilities = new Utilities();
}
}
Each of them having a list of sub-model like this:
public class Weapons
{
public BattleAxe BattleAxe { get; set; } = new BattleAxe();
public Bomb_Missile Bomb_Missile { get; set; } = new Bomb_Missile();
// etc... Around 20 to 25
}
And finally the ended model which is the exact equivalent of each json files but may have very different properties :
public class BattleAxe
{
public string[] Normal { get; set; } = new string[0];
public string[] DescriptiveAdjective { get; set; } = new string[0];
public string[] Material { get; set; } = new string[0];
public string[] Type { get; set; } = new string[0];
public string[] Title { get; set; } = new string[0];
public string[] Of { get; set; } = new string[0];
public string[] NormalForTitle { get; set; } = new string[0];
}
Since the MS Json deserializer does not support the conversion to a $type as Newtonsoft before, I tried to populate the values using reflection too like this (I've removed all the null-check for code readability) :
public static void Load()
{
Names = new Names();
foreach (var category in Names.GetType().GetProperties())
{
if (category is not null && !(category.GetGetMethod()?.IsStatic ?? false))
{
var categoryType = category.PropertyType;
foreach (var item in category.PropertyType.GetProperties())
{
var itemType = item.PropertyType;
var subTypeData = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(File.ReadAllText($"./Assets/Names/{categoryType.Name}/{itemType.Name}.json"));
var concreteObj = Activator.CreateInstance(itemType);
foreach (var key in subTypeData.Keys)
{
if (itemType.GetProperty(key) is not null && concreteObj is not null)
{
var prop = concreteObj.GetType().GetProperty(key);
var convertedValue = ConvertJsonType(subTypeData[key], subTypeData[key].ValueKind, out var isReferenceType);
// It fails here
prop.SetValue(
isReferenceType ? convertedValue : null,
!isReferenceType ? convertedValue : null
);
}
}
item.SetValue(concreteObj, null);
}
}
}
}
So it fails at the prop.SetValue(...) of the deepest object in the hierarchy with a different error depending on the type of value to set.
If it is a reference, it throws a System.Reflection.TargetException : 'Object does not match target type' Exception
And if it is value, it throw a System.Reflection.TargetException : 'Non-static method requires a target.'
Knowing that I do not have problems around the deserialization as shown here, only the fact that I use a dynamic type (and my instinct tells me it is actually the problem...)
I do not add the ConvertJsonType(...) body as it is functional and really simple
I am more interested in the 'why' than the 'how' so if you can explain me the 'theory' behind the problem, that would help quite a lot :)
Thank you!
PS: I know I can simplify the things in a more readable/performant way but I must achieve it with reflection for personal learning :)
Same for the System.Text.Json namespace, I do not intend to switch back to Newtonsoft for that
When calling SetValue(instance, value) you should pass the object which property should be set.
It's a wild guess, but you could try this:
prop.SetValue(concreteObj,
!isReferenceType ? convertedValue : null);
Because you want to fill the properties of concreteObj, not the value it self.
If you look at the object prop it was a return value of concreteObj.GetType().GetProperty(key);. If you look at it close, The GetProperty is a method from Type which isn't bound to any instance. So that's why you need to pass the instance of the object as the first parameter.
I mean this in a positive way: The itemType.GetProperty(key) is called every iteration, it will be the same value each iteration, you could bring it before the loop.
As docs state TargetException is thrown when:
The type of obj does not match the target type, or a property is an instance property but obj is null.
Passing null for obj in SetValue is valid when you are trying to set value for static property, not an instance one. Property type being a reference one has nothing to do with property being instance or static one so your call should look something like:
prop.SetValue(concreteObj, convertedValue);
Also your item.SetValue(concreteObj, null); does not look right cause concreteObj should be second argument in this call. Something like this:
item.SetValue(Names, concreteObj);
Also if you want only instance properties you can provide BindingFlags to get only instance properties:
foreach (var category in Names.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
Also I would say that category is not null check is redundant so in pair with providing BindingFlags you should remove the if completely.
I was looking at Xamarin code base (in this case StackLayout class) and I came with this strange line of code in C#, which I couldn't understand the syntax:
var layout = new StackLayout()
{
Children =
{ // What is this!!!?
new Label()
{
Text = "Hello",
},
new Entry()
{
Text = "Hi"
},
}
};
The code that I don't understand is the way it initializes the Children property. Children is a get-only property with no setter.
Not only it is being initialized, but also there is no new List<> before {.
Resharper can convert it to use .Add() instead of this initialization. So it seems it is not an initialization.
I think there's something added to C# which I'm not aware of!
This is just a variation of the "initializer syntax" for collections, valid when initializing property values in a new instance.
The initializer syntax in general allows assigning values to properties when using the new operator. Then, in that context, the collection initializer syntax maps an assignment to a sequence of calls to an Add() method (if present, which it is in this case).
This isn't unique to Xamarin. Here's a simple C# example:
public class Class1
{
public IList<string> List { get; } = new List<string>();
public static Class1 M()
{
return new Class1
{
List =
{
"foo", "bar"
}
};
}
}
Let's try to run your code with these classes:
public class StackLayout
{
public object[] Children;
}
public class Label
{
public string Text;
}
public class Entry
{
public string Text;
}
If I do that I get the following error:
CS1061 'object[]' does not contain a definition for 'Add' and no extension method 'Add' accepting a first argument of type 'object[]' could be found (press F4 to add a using directive or assembly reference)
If I change it to this:
public class StackLayout
{
public List<object> Children;
}
Then I get the error:
NullReferenceException: Object reference not set to an instance of an object.
So finally I do this:
public class StackLayout
{
public List<object> Children = new List<object>();
}
That works.
So the syntax you've shown is shorthand for calling an .Add(...) method.
This is a Feature of C# 6 and the collection initializer Syntax. In short: If the Item does have an Add-Method than the Compiler will use it to initialize the Collection. See Extension Add methods in collection initializers in the C# 6 release notes.
Not only for IList:
internal class Program
{
private static void Main(string[] args)
{
var p = new Person
{
Name = "Mark",
Address = //<<<<<<<< HERE
{
Number = 3,
Street = "Long street"
}
};
}
}
public class Person
{
public string Name { get; set; }
public Address Address { get; set; }
}
public class Address
{
public int Number { get; set; }
public string Street { get; set; }
}
Btw, could somebody tell me the name of this C# feature?
I have a Dictionary<string, object> which holds a property name as string and it's value as object. I also have a Bind method extension which, through reflection, sets that propery name with its corresponding value:
public static T Bind<T>(this T #this,
Dictionary<string, object> newValues,
params string[] exceptions) where T : class
{
var sourceType = #this.GetType();
foreach (var pair in newValues.Where(v => !exceptions.Contains(v.Key)))
{
var property = sourceType.GetProperty(pair.Key,
BindingFlags.SetProperty |
BindingFlags.Public |
BindingFlags.Instance);
var propType = Nullable.GetUnderlyingType(property.PropertyType) ??
property.PropertyType;
property.SetValue(#this, (pair.Value == null) ? null :
Convert.ChangeType(pair.Value, propType), null);
}
return #this;
}
For instance, consider a class like this:
public class User
{
public string Name { get; set; }
public DateTime Date { get; set; }
}
Everything runs fine, except when I got a class with a property name of another object, like this:
public class User
{
public string Name { get; set; }
public DateTime Date { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string PostalCode { get; set; }
}
So, if I try to send a Name property name, ok, but I got problems with composite property names, like Address.PostalCode.
Can you advise a way to handle that situation?
EDIT #1:
To summarize the problem: calling sourceType.GetProperty("Name", ...) in the context of a User class instance correctly allows to set its value, but it doesn't work using a sourceType.GetProperty("Address.PostalCode", ...) in same instance.
EDIT #2:
A more complete example should be:
var user = new User{ Address = new Address() };
var values = new Dictionary<string, object>
{
{ "Name" , "Sample" },
{ "Date" , DateTime.Today },
{ "Address.PostalCode", "12345" } // Here lies the problem
}
user.Bind(values);
My guess is that Convert.ChangeType only works for objects implementing IConvertible. Thus, I'd just add a check, and only use Convert.ChangeType if pair.Value has a type that implements IConvertible. Furthermore, afaik Convert does not use overloaded conversion operators, so you can save this check whenever pair.Value is not a struct, i.e.
object value;
if (pair.Value == null) {
value = null;
} else {
value = pair.Value.GetType().IsStruct ? Convert.ChangeType(pair.Value, propType) : pair.Value;
}
...
There are many binding engines out there, WPF, ASP.NET MVC, winforms in the core .NET and who knows how many others, you can check out all their source codes and documentation about their syntax.
Let's see the most simple case. Let's say that the variable X holds an object and you have the binding expression "A.B.C". Let's split up the binding path, the first part is "A". So you use reflection to get the property named "A" in X, and you put that other object into X. Now comes the second part, "B", so let's find a property named "B" in (the new) X. You find that, and put that into X. Now you get to the final part, "C", and now you can either read or write that property in X. The point is that you don't need recursion or anything, it's just a simple loop, you iterate over the parts of the binding expression, evaluate them, and you keep the current object in the same variable.
But the fact is that it can get much more complex than that. You could ask for array indexing, like "A.B[2].C". Or what if you have a path "A.B", and X.A is null, what do you do? Instantiate X.A, but what if it lacks a public parameterless constructor?
I want you to see that it can be a very complex problem. You have to specify a syntax and rules, and then implement that. You didn't specify in your question the exact syntax and rules you want to use. And if it happens to be more than the simple case I mentioned above, then the solution could be too lengthy.
I was able to solve it identifying if the property name have a period and recurring it:
public static T Bind<T>(this T #this,
Dictionary<string, object> newValues,
params string[] exceptions) where T : class
{
var sourceType = #this.GetType();
var binding = BindingFlags.Public | BindingFlags.Instance;
foreach (var pair in newValues.Where(v => !exceptions.Contains(v.Key)))
{
if(pair.Key.Contains("."))
{
var property = sourceType.GetProperty(
pair.Key.Split('.').First(),
binding | BindingFlags.GetProperty);
var value = property.GetValue(#this, null);
value.Bind(new Dictionary<string, object>
{
{
String.Join(".", pair.Key.Split('.').Skip(1).ToArray()),
pair.Value
}
});
}
else
{
var property = sourceType.GetProperty(pair.Key,
binding | BindingFlags.SetProperty);
var propType = Nullable.GetUnderlyingType(property.PropertyType) ??
property.PropertyType;
property.SetValue(#this, (pair.Value == null) ? null :
Convert.ChangeType(pair.Value, propType), null);
}
}
return #this;
}
Usage:
var user = new User {Address = new Address{ User = new User() }};
var values = new Dictionary<string, object>()
{
{"Name", "Sample"},
{"Date", DateTime.Today},
{"Address.PostalCode", "12345"},
{"Address.User.Name", "Sub Sample"}
};
user.Bind(values);
public class User
{
public string Name { get; set; }
public DateTime Date { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string PostalCode { get; set; }
public User User { get; set; }
}
I have created custom attribute that is part of MEF where I would like to define list of ids that are relevant to the class so I can query on them.
Also the class has to contain definition within itself, this is important that is why i thought about using:
[SignalSystemData("ServiceIds", new List<int>(){1})]
How shall I proceed?
My implementation of search is as follows:
var c = new Class1();
var v = c.EditorSystemList;
foreach (var lazy in v.Where(x=>x.Metadata.LongName=="ServiceIds"))
{
if (lazy.Metadata.ServiceId.Contains(serviceIdToCall))
{
var v2 = lazy.Value;
// v2 is the instance of MyEditorSystem
Console.WriteLine(serviceIdToCall.ToString() + " found");
}else
{
Console.WriteLine(serviceIdToCall.ToString() + " not found");
}
}
My Export class with definition should look like this:
[Export(typeof(IEditorSystem))]
[SignalSystemData("ServiceIds", new List<int>{1})]
public class MyEditorSystem1 : IEditorSystem
{
void test()
{
Console.WriteLine("ServiceID : 1");
}
}
public interface IEditorSystem
{
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class SignalSystemDataAttribute : ExportAttribute
{
public SignalSystemDataAttribute(string longName, List<int> serviceId)
: base(typeof (IEditorSystem))
{
LongName = longName;
ServiceId = serviceId;
}
public string LongName { get; set; }
public List<int> ServiceId { get; set; }
}
public interface IEditorSystemMetadata
{
string LongName { get; }
List<int> ServiceId { get; }
}
To get around the compile time constant issue, you have the following choices:
Use a specially formatted string (i.e. a comma separated list of integers, as you already suggested).
Use a number of overloads, each with a different number of arguments for the IDs. This will get messy if you have too many IDs to be passed.
Use params int[] ids as the last argument to your constructor. This will work, but is not CLS compliant - if that matters for you.
Most easily use an array int [] argument.
Of course you could also use a combination of the above. Having a couple of overloads with say 1 to 5 ID arguments and providing a string argument or params int[] argument for those (hopefully) corner cases, where the overload arguments are not sufficient.
Update: Just found this question/answer. Might not be duplicate as such, but deals with the same issue (mostly).