How to access class member by string in C#? - c#

Is there a way to access member by a string (which is the name)?
E.g. if static code is:
classA.x = someFunction(classB.y);
but I only have two strings:
string x = "x";
string y = "y";
I know in JavaScript you can simply do:
classA[x] = someFunction(classB[y]);
But how to do it in C#?
Also, is it possible to define name by string?
For example:
string x = "xxx";
class{
bool x {get;set} => means bool xxx {get;set}, since x is a string
}
UPDATE, to tvanfosson, I cannot get it working, it is:
public class classA
{
public string A { get; set; }
}
public class classB
{
public int B { get; set; }
}
var propertyB = classB.GetType().GetProperty("B");
var propertyA = classA.GetType().GetProperty("A");
propertyA.SetValue( classA, someFunction( propertyB.GetValue(classB, null) as string ), null );

You need to use reflection.
var propertyB = classB.GetType().GetProperty(y);
var propertyA = classA.GetType().GetProperty(x);
propertyA.SetValue( classA, someFunction( propertyB.GetValue(classB,null) as Foo ), null );
where Foo is the type of the parameter that someFunction requires. Note that if someFunction takes an object you don't need the cast. If the type is a value type then you'll need to use (Foo)propertyB.GetValue(classB,null) to cast it instead.
I'm assuming that we are working with properties, not fields. If that's not the case then you can change to use the methods for fields instead of properties, but you probably should switch to using properties instead as fields shouldn't typically be public.
If the types aren't compatible, i.e., someFunction doesn't return the type of A's property or it's not assignable, then you'll need to do a conversion to the proper type. Similarly if the type of B isn't compatible with the parameter of the function, you'll need to do the same thing.
propetyA.SetValue( classA, someFunction(Convert.ToInt32( propertyB.GetValue(classB,null))).ToString() );

Related

Trying to convert an C# anonymous type to a strong type

I'm trying to convert some anonymous type back to its original strong type class.
I have some legacy code (which I cannot touch) which create an anonymous class:
public class Cat : FooId
{
public int Id { get; set; }
public string Name { get; set; }
}
var result = new
{
Id = Mapper.Map<TFooId>(someCat)
};
NOTE: I've tried to make this fake class and interface similar to my code.
This then gives me:
result.GetType().ToString() : <>f__AnonymousType1``1[MyProject.Cat]
From here, I'm not sure how to convert this back to a MyProject.Cat instance?
I've tried (and fails):
(MyProject.Cat)result
(dynamic)result
but both fail. The dynamic doesn't throw an error ... but I can't access any properties in it.
C# is a statically typed language, and those two types are not in any way related to one another. Unless you're able to modify the code which defines those types, the way you'd convert from one to the other would be to create a new instance of the target type and populate it from the source object.
For example:
var resultCat = new Cat { Id = result.Id };
Edit: From comments it looks like it may be possible that the Id property on the result object may be an instance of Cat or some other object? You're going to need to do some debugging to find out what your types are.
But the overall concept doesn't really change. If you have an instance of Cat in your results then you can use that instance. If you don't then in order to create one you'd need to create a new instance and populate it with the data you have. Even if two types are intuitively or semantically similar, they are different types.
It's true what David said with regard to the fact that C# is a statically-typed language and that the new instance should be populated from the source the way he suggested.
However, there are work-arounds (though less performant) for that, such as reflection.
Consider you have a console app where you have defined ObjectExtensions as follows:
public static class ObjectExtensions
{
public static TOut Map<TOut>(this object #in)
where TOut : new()
{
TOut #out = new TOut();
if (#in?.GetType() is Type tin)
{
Type tout = typeof(TOut);
foreach ((PropertyInfo pout, PropertyInfo pin) in tout.GetProperties().Join(tin.GetProperties(), pi => pi.Name, pi => pi.Name, (pout, pin) => (pout, pin)))
{
pout.SetValue(#out, pin.GetValue(#in));
}
}
return #out;
}
}
And Class1 as follows:
public class Class1
{
public string A { get; set; } = "A";
public string B { get; set; } = "B";
public string C { get; set; } = "C";
public override string ToString()
{
return $"{{A={A}, B={B}, C={C}}}";
}
}
You will be able to map your anonymous type back to its original strongly-typed class like this:
Console.WriteLine(new { A = "Anonymous A", B = "Anonymous B", C = "Anonymous C" }.Map<Class1>());
Therefore the bloc above should show the following output:
{A=Anonymous A, B=Anonymous B, C=Anonymous C}
In this case, of course, I have assumed that Class1 (Cat in your example) must have a public parameterless constructor. That may not always be the case. There are more sophisticated scenarios of course that might involve other techniques for creating the object such as cloning or dependency injection. Just saying that the idea of yours is possible.

How to name tuple properties?

How and "could be" organized return from the method which returns tuple type with the name of parameters,
as an example
private static Tuple<string, string> methodTuple()
{
return new {Name = "Nick", Age = "Twenty"}; /*exception because need to new Tuple<string, string>(){Item1 = "Nick", Item2 = "Twenty"}o*/
}
and call parameters like methodTuple.Name not like methodTuple.Item1....N
Is this possible or not?
UPD: I want to create object with named parameters without new named type.
In C# 7.0 (Visual Studio 2017) there is a new option to do that:
(string first, string middle, string last) LookupName(long id)
Starting C# v7.0, it is now possible to give custom name to tuple properties. Earlier they used to have default names like Item1, Item2 and so on. Let's look at few variations which is now possible:
Naming the properties of Tuple Literals:
var personDetails = (Name: "Foo", Age: 22, FavoriteFood: "Bar");
Console.WriteLine($"Name - {personDetails.Name}, Age - {personDetails.Age}, Favorite Food - {personDetails.FavoriteFood}");
The output on console:
Name - Foo, Age - 22, Favorite Food - Bar
Returning Tuple (having named properties) from a method:
static void Main(string[] args)
{
var empInfo = GetEmpInfo();
Console.WriteLine($"Employee Details: {empInfo.firstName}, {empInfo.lastName}, {empInfo.computerName}, {empInfo.Salary}");
}
static (string firstName, string lastName, string computerName, int Salary) GetEmpInfo()
{
//This is hardcoded just for the demonstration. Ideally this data might be coming from some DB or web service call
return ("Foo", "Bar", "Foo-PC", 1000);
}
The output on console:
Employee Details: Foo, Bar, Foo-PC, 1000
Creating a list of Tuples having named properties:
var tupleList = new List<(int Index, string Name)>
{
(1, "cow"),
(5, "chickens"),
(1, "airplane")
};
foreach (var tuple in tupleList)
Console.WriteLine($"{tuple.Index} - {tuple.Name}");
Output on console:
1 - cow
5 - chickens
1 - airplane
Note: Code snippets in this post are using string interpolation feature of C# which was introduced in version 6 as detailed here.
You need to declare a helper class to do so.
public class MyResult
{
public string Name { get; set; }
public string Age { get; set; }
}
What you're trying to return is an anonymous type. As the name suggests you don't know what its name is, so you can't declare your method to return it.
Anonymous Types (C# Programming Guide)
You cannot declare a field, a property, an event, or the return type
of a method as having an anonymous type. Similarly, you cannot declare
a formal parameter of a method, property, constructor, or indexer as
having an anonymous type. To pass an anonymous type, or a collection
that contains anonymous types, as an argument to a method, you can
declare the parameter as type object. However, doing this defeats the
purpose of strong typing. If you must store query results or pass them
outside the method boundary, consider using an ordinary named struct
or class instead of an anonymous type.
Update
C#7 introduces Tuple support built into the language and it comes with named tuples
(string name, int age) methodTuple()
{
(...)
}
Read more on learn.microsoft.com: https://learn.microsoft.com/en-us/dotnet/articles/csharp/csharp-7#tuples
This is not possible with Tuple, no. You'll need to create your own new named type to do this.
Now you can do it with tuple Name in C#
For Lambda Expression:
private static (string Name, string Age) methodTuple() => ( "Nick", "Twenty" );
Or
private static (string Name, string Age) methodTuple()
{
return ( "Nick", "Twenty" );
}
Do not use class type for Tuple. Use primitive type to set the name in Tuple.
I usually create a new type that derives from Tuple, and map your explicit properties to return the base class's ItemX properties.
eg:
public class Person : Tuple<string, string>
{
public Key(string name, string age) : base(name, age) { }
public string Name => Item1;
public string Age => Item2;
}
Unfortunately, this is not possible using the "Tuple" type, as it is defined as "Item1...N" in MSDN. So this exception is valid.
This method can compile in 3 ways:
1.) Change return type to object - this will create an "anonymous" type, which you can then use later. It is not particularly useful if you want to access the "Name" or "Age" property later without some additional work.
2.) Change return type to dynamic - this will let you access the "Name" and "Age" property, but will make the entire program (just the DLL where this method is located really) slightly slower as the use of dynamic necessitates throwing out some strong typing.
3.) Create a class and use it as teh return type.
Sample code here:
private static object ObjectTuple()
{
return new { Name = "Nick", Age = "Twenty" };
}
private static dynamic DynamicTuple()
{
return new { Name = "Nick", Age = "Twenty" };
}
private static Temp TempTuple()
{
return new Temp{ Name = "Nick", Age = "Twenty" };
}
class Temp
{
public string Name { get; set; }
public string Age { get; set; }
}
As per me, when you want to return or get many things from a single method, better make its return type as CLASS but if you intend to use Tuple which itself is Class then for better naming this new class should inherit from Tuple. e.g. mentioned below.
public CustomReturn ExecuteTask( int a, string b, bool c, object d )
{
// Calling constructor of CustomReturn Class to set and get values
return new CustomReturn(a,b,c,d);
}
internal class CustomReturn
// for tuple inherit from Tuple<int,string,bool,object,double>
{
//for tuple public int A{ get {this.Item1} private set;}
public int A{get;private set;}
public string B{get;private set;}
public bool C{get;private set;}
public object D{get;private set;}
public CustomReturn (int a, string b, bool c, object d )
// use this line for tuple ": base( obj, boolean )"
{
this.A = a;
this.B = b;
this.C = c;
this.D = d;
}
}
Main(args)
{
var result = ExecuteTask( 10, "s", true, "object" );
// now if u have inherited Tuple for CustomReturn class then
// on doing result. you will get your custom name as A,B,C,D for //Item1,Item2,Item3,Item4 respectively also these Item1,Item2,Item3,Item4 will also be there.
}

Class of type <T> with nullable value of Type<T>

I have the need to make a class that can hold the value for the following types: int?, decimal?, date, bool? string.
I want to be able to do soemthing like:
var x = new MyClass<int?>()
x.Value = null;
x.value = 99;
// or
var y = new MyClass<bool?>();
y.Value = null;
y.Value = true
// and so on
I have been trying to create a class of type T along the lines of:
public class TestClass<T>
{
public T? Value{ get; set; }
}
I want to use Value to take any of the allowed types but I can't make T nullable. The error is:
Only non nullable value type could be underlying of Sysytem.Nullable
is there anyway of doing what I'm trying to achieve?
Try this:
public class TestClass<T> where T : struct
{
public T? Value{ get; set; }
}
Classes cannot be nullable as class is a reference type. You have to add constraint to your generic class to allow only structs (which can be nullable).
string is a reference type and your class won't work with string it this case as it can't be nullable.
The problem is that you are passing a nullable into your generic, and then trying to make it nullable.
Try this:
public class TestClass<T>
{
public T Value{ get; set; }
}
Now when you do this:
var x = new MyClass<int?>();
Value will be defined as int? so you can use it in the way you want. If you define it as
var x = new MyClass<int>();
Value will be defined as int - and won't accept nulls.

What's the return type of an anonymous class

I have a class that used to have a string return type. Now I find I need to return more than a string. I was thinking to return something like below:
public string Test()
{
return ( new { ID = 5, Name= "Dave" } );
}
Is this even possible and if so then what would be the return type? I know it's not string ..
As others have said, the best thing to do here is to make a nominal type. I would suggest that the nominal type have the same characteristics as an anonymous type; that is, you should consider making the type immutable and consider making it exhibit value equality.
It is possible to return an anonymous type as object and then use the instance returned elsewhere using a variety of sneaky techniques. You can cast the object to "dynamic" (in C# 4) and then use the properties of the anonymous type, but this is slow and lacks compile-time type checking.
You can also use the "cast by example" trick, which does get you compile-time type checking. However, that trick only works when the anonymous source object and the anonymous example object come from the same assembly.
static T CastByExample<T>(object source, T example) where T : class
{
return source as T;
}
static object ReturnsAnonymous() { return new { X = 123 }; }
static void DoIt()
{
object obj = ReturnsAnonymous();
var example = new { X = 0 };
var anon = CastByExample(obj, example);
Console.WriteLine(anon.X); // 123
}
See how sneaky that is? We use method type inference and local variable type inference to tell the compiler "these two things are the same type". This lets you export an anonymous type as object and cast it back to anonymous type.
But you probably should not do this; if you're resorting to such sneaky tricks then you should simply be defining a nominal type in the first place. Also, like I said, the trick only works if the example and the source objects were created in code in the same assembly; two "identical" anonymous types in two different assemblies do not unify to be the same type.
The object that you return does have a class, but it's anonymous so you can't specify it in the code. You just have to return it as an object reference:
public object Test() {
return new { ID = 5, Name= "Dave" };
}
Note that the anonymous type is unknown outside the scope of the method, so reflection is the only way to access its properties.
If you want to be able to use the returned object conveniently, you should declare a class:
public class TestResult
{
public int ID { get; set; }
public string Name { get; set; }
}
public TestResult Test() {
return new TestResult() { ID = 5, Name= "Dave" };
}
Another alternative is to use an existing class, if it fits your purpose. A KeyValuePair is close to what you use, but then the properties will of course be named Key and Value instead of ID and Name:
public KeyValuePair<int, string> Test() {
return new KeyValuePair<int, string>(5, "Dave");
}
This isn't possible as the anonymous class is only valid within the current context. If you need to return an object then you'll need to create a real class.
I'm assuming you left string as the return type by accident.
Anonymous type are class type that are derived directly from object.
You can return it from method as object as return type.
Have a look at this.
No, it's not possible. Your options are:
Define a real class for the return value,
Use System.Tuple, or
Use out parameters (probably the least good option).
You can make a struct (or class) for this.
public struct IdAndName
{
public int Id;
public string Name;
public IdAndName(int id, string name)
{
ID = id;
Name = name;
}
}
You could also use a Tuple<T1, T2>, (but that's not recommended as the properties aren't named.
class NewString
{
public int ID { get; set; }
public string Name { get; set; }
}
public NewString Test()
{
return ( new NewString() { ID = 5, Name = "Dave" } );
}
:)

Making a property compulsory

Is there any way to make a property that is defined in a class compulsory?
I want the compiler to complain if the object is declared and used without the compulsory property.
Best approach I would think would be to not specify a default constructor.
public class MyClass
{
public MyClass(string myProp)
{
MyProp = myProp;
}
public string MyProp { get; set; }
}
A way might be to implement a constructor that forces the creation of an object to have an initial value for a property:
class Demo
{
public int P {get; set;}
Demo(int p)
{
P = p;
}
}
Now the client can only create the object by passing a value for p:
var d = new Demo(42);
How about adding a constructor to the class that would take a value for the property?
public class MyClass
{
public int Mandatory {get; set;}
public int Optional {get; set;}
public MyClass(int mandatory)
{
Mandatory = mandatory;
}
public MyClass(int mandatory, int optional)
{
Mandatory = mandatory;
Optional = optional;
}
}
This way, the class can be instantiated using either the one or two-parameter constructor, so the user will have to specify a value for the Mandatory property.
MyClass x = new MyClass(); // does not compile
MyClass x = new MyClass(1); //Mandatory = 1; Optional = 0 (default value)
MyClass x = new MyClass(1,2);//Mandatory = 1; Optional = 2
No, you can't force somebody to provide information to a property.
However, you can go the normal route, and make the constructor require the information. That way, the object can't be constructed without providing the information you need.
You can just add a constructor to your class that accepts one parameter - the value for your property. Like this, the property is always initialized after an instance of the class is created.
make default constructor private and define constructor that will get needed value for your property
public YourClass(string value)
{
YourCompulsaryProperty = value;
}
public string YourCompulsaryProperty{get; set;}

Categories