I have a function to return relevant text from various windows controls.
I was hoping to be able to create something a little more elegant, and less static.
Here's code that functions the way I want it to:
public string returnText(Control controller)
{
string bob = controller.GetType().Name.ToString();
if (bob == "TextBox")
return ((TextBox)controller).Text;
else if (bob == "ComboBox")
return ((ComboBox)controller).SelectedValue.ToString();
else
return "Invalid Object";
}
What I'd like to do is something like this:
Calling code:
TextBoxValue del = x => x.Text;
ComboBoxValue com = x => x.SelectedValue.ToString();
bob.returnText2(this.cboUnitType, com);
bob.returnText2(this.txtCurbAdj, del);
Function:
public string returnText2<T>(T controller, Func<T, string> lambda )
{
return lambda(controller);
}
I'm guessing I'm doing the generic side of things wrong, but I haven't been able to figure out the right way to do it.
Or maybe I'm completely out to lunch here.
Here's a cleaner (and clearer) version of your original code. It doesn't use any reflection, generics or ToString calls. Instead, it uses a pattern matching switch statement:
public static string ReturnText(Control control)
{
switch (control)
{
case TextBox tb:
return tb.Text;
case ComboBox cb:
return cb.SelectedText;
//etc.
default: return string.Empty;
}
}
By the way, your use of the name controller for a variable of type Control is misleading; controller has real meaning in other contexts.
An alternative would be to create a Dictionary<Type, Func<Control, string>> where an entry would look like {typeof(Textbox), c=>((TextBox)c).Text}, but I think that would be a lot less clear than just using a switch like I've shown.
One other thing: You could make this function an extension method on the Control type (by putting it in a static class and putting the keyword this before Control in the parameter list). I'd probably rename it GetTextValue. At that point, you could just say var textValue = anyControl.GetTextValue(); for any control on your page. Unfortunately, there isn't an extension property yet in C#
Using reflection is pretty easy. What you do is use the type of the class to retrieve their properties. Then you request the value of that property on an object and you got the value.
here a quick simple reflection. What it does is get the type of the object, request the property named SelectedValue then query the combobox object to retrieve the value and finally convert as string.
var combobox = this.cboUnitType;
var value = combobox.GetType().GetProperty("SelectedValue").GetValue(combobox ).ToString();
Here the same thing made into a function and quite generic plus has possible error handling
private string GetValue(object obj, string property)
{
var value = "";
try
{
value = obj.GetType().GetProperty(property).GetValue(obj).ToString();
}
catch { }
return value;
}
Related
I have a function that returns a particular type. Ex. FaxFile. In that object there are several different formats to download ex: PDF, LargeJpg, SmallJpg, etc.
I can call the download a pdf like this.
public FaxFile DownloadFaxPDFById(int faxId)
{
return DownloadFaxById(faxId).Pdf;
}
What I am wanting to do is be able to pass in the property of the object ex. LargeJpg Format to download.
Sudo Code
public FaxFile DownloadFaxTypeById(int faxId, property)
{
return DownloadFaxById(faxId).property;
}
How do I do this?
you can use reflection.
var resultObj = DownloadFaxById(faxId);
var result = resultObj.GetType().GetProperty("<propertyName>").GetValue(resultObj);
please note that you need to cast result to appropriate object
You don't want to do this at the first place. Just use DownloadFaxById(faxId).Pdf/LargeJpg/...; in the call place or, if you don't want to expose class returned by DownloadFaxById either subclass or use aggregation and expose Pdf, LargeJpg, ... in this new class.
Another way of doing this besides those already posted as answers is to use lambda expressions. Not sure what's the type of the downloaded object, so replace DownloadedObjectType with your own.
public FaxFile DownloadFaxTypeById(int faxId, Expression<Func<DownloadedObjectType, FaxFile>> expression) {
if (!(expression.Body is MemberExpression)) {
throw new ArgumentException("Invalid expression");
}
return expression.Compile().Invoke(DownloadFaxById(faxId));
}
You then call it as
DownloadFaxTypeById(faxId, obj => obj.Pdf)
However looks much uglier than simply calling
DownloadFaxTypeById(faxId).Pdf
Except for maybe providing you some control over what properties can the caller retrieve, limiting them to that specific return type and only those that are actually available for that type. This way for a subset of possible errors (like referencing non-existing property) you get compile time errors rather than all runtime as in cases using reflection.
I would recommend to create an enum for this and pass it to your function.
Inside your function simple use switch.
This means more programming, but the usage of your function is much clearer.
This has also the benefit that you later can change the code how you obtain each format without taking care of the consumer of your function.
enum FaxFileType
{
PDF,
LargeJpg,
SmallJpg,
...
}
public FaxFile DownloadFaxById(int faxId, FaxFileType faxFileType)
{
switch (faxFileType)
{
case FaxFileType.PDF:
return DownloadFaxById(faxId).PDF;
case FaxFileType.LargeJpg:
return DownloadFaxById(faxId).LargeJpg;
case FaxFileType.SmallJpg:
return DownloadFaxById(faxId).SmallJpg;
...
default:
throw new NotSupportedException();
}
}
You should probably just use a switch statement for each format type you support from an enum. However if you really want to use reflection, you can so it as follows:
void Main()
{
var foo = new Foo();
foo.GetType().GetProperty("MyProp").SetValue(foo, "Test");
var val = foo.GetType().GetProperty("MyProp").GetValue(foo);
Console.WriteLine(val);
}
public class Foo
{
public String MyProp { get; set; }
}
Sometimes, I need to get the string equivalent of a property name or expression. Most of the time this is for some serialization or ORM-mappings. The use is irrelevant. Instead of typing the string as a "magic string", e.g
var sPropertyName = "PropertyId";
I use reflection, so that if it changes, it is automatically updated. Example:
public void test()
{
var sPropertyName = GetPropertyName<Property>(x => x.PropertyId);
}
public static string GetPropertyName<T>(Expression<Func<T, object>> selector)
{
return (PropertyInfo)GetMemberInfoBySelector(selector);
}
public static MemberInfo GetMemberInfoBySelector(Expression selector)
{
Expression body = selector;
if (body is LambdaExpression)
{
body = ((LambdaExpression)body).Body;
}
switch (body.NodeType)
{
case ExpressionType.Call:
return (((System.Linq.Expressions.MethodCallExpression)body).Method);
case ExpressionType.MemberAccess:
return ((PropertyInfo)((MemberExpression)body).Member);
case ExpressionType.Convert:
{
UnaryExpression unaryExp = (UnaryExpression)body;
var op = unaryExp.Operand;
System.Linq.Expressions.MemberExpression memberExp = (MemberExpression)op;
return memberExp.Member;
}
default:
throw new InvalidOperationException("Cannot retrieve member info from selector!");
}
}
Sometimes, this is called quite often for batch-processing, or similar processing. I find it a waste that this resorts to reflection, when the value can actually be a constant. In cases where it is used a lot, this can slow things down quite a bit.
I know there exists a tool for ASP.Net MVC - T4MVC which scans the files and creates static classes with the property names.
In this case, something ideal would be where I could just use say
var sPropertyName = GeneratedMetaData.MyNamespace.Property.PropertyId
Is this somehow possible? I know I can store these constants myself on app start, but wanted to know if there is something automatic like T4MVC is.
Or maybe other approaches which does the same thing?
I have a method as following:
public void MyMethod(object obj){
// implement
}
And I call it like this:
MyMethod(new { myparam= "waoww"});
So how can I implement MyMethod() to get myparam value?
Edit
I use this:
dynamic d= obj;
string param = d.myparam;
but the error rise :
'object' does not contain a definition for 'myparam'
also I use breakpoint and I see the d have myparam string property.
And is there any way to check dynamic type to if contain any property like this:
if(d.contain(myparam))?
Edit II
This is my main code:
public static MvcHtmlString SecureActionLink(this HtmlHelper htmlHelper,
string linkText, string actionName, string controllerName,
object routeValues, object htmlAttributes) {
string areaName =
(string)htmlHelper.ViewContext.RouteData.DataTokens["area"];
dynamic areaObject = routeValues;
if(areaObject != null && !string.IsNullOrEmpty(areaObject.area))
areaName = areaObject.area;
// more
}
and call it as:
<p>#Html.SecureActionLink("Secure Link between Areas", "Index", "Context",
new { area = "Settings" }, null)</p>
And Error is:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'object' does not contain a
definition for 'area'
Line 303: dynamic areaObject = routeValues;
Line 304:
Line 305: if(areaObject != null && !string.IsNullOrEmpty(areaObject.area))
Line 306: areaName = areaObject.area;
Line 307:
Source File: D:\Projects\MyProject\HtmlHelpers\LinkExtensions.cs Line: 305
Edit III
This is my AssemblyInfo of HtmlHelper definition:
[assembly: AssemblyTitle("MyProject.Presentation")]
[assembly: InternalsVisibleTo("cpanel.MyProject.dev")]
but there is an error yet: 'object' does not contain a definition for 'area'
I use different assemblies but how can it possible, when I use breakpoint I can see that my dynamic areaobject have area name property and also I can see the value of that, but the error say: 'object' does not contain a definition for 'area' I can't figure it how it can be possible?
Edit
I change the assembly and now dynamic type is internal but the error remains as before
Use this one:
string area = areaObject.GetType().GetProperty("area").GetValue(areaObject, null);
Well, you could use dynamic typing if you're using C# 4:
public void MyMethod(object obj) {
dynamic d = obj;
Console.WriteLine(d.myparam);
}
It does beg the question of why you're not using a named type though. Anonymous types aren't really designed to be shared among different methods like this.
EDIT: Note that if this is in a different assembly to the original code creating the object, you'll need to use [InternalsVisibleTo] as anonymous types are internal.
First off, as others have said: don't do this in the first place. That's not how anonymous types were intended to be used.
Second, if you are bent upon doing it, there are a number of ways to do so. The slow and dangerous way is to use dynamic, as others have said.
The fast and dangerous way is to use "cast by example:
static T CastByExample<T>(object obj, T example)
{
return (T)obj;
}
static void M(object obj)
{
var anon = CastByExample(obj, new { X = 0 });
Console.WriteLine(anon.X); // 123
}
static void N()
{
M(new { X = 123 });
}
is there any way to check dynamic type to if contain any property?
Use Reflection. Of course, if you are going to use Reflection then there is no need to use dynamic in the first place. You use dynamic to avoid using Reflection, so if you are going to be using Reflection anyways, you might as well just keep on using it.
It sounds like you are trying to do something that is hard to do in C#. I would reevaluate whether you want to be doing that, and if you do, whether C# is the language for you. A dynamic language like IronPython might be a better fit for your task.
Everybody says "don't do it in the first place", but this is exactly what asp.mvc does!
(Don't get me wrong I don't like it myself, but if you are writing custom html helpers you want to call them the way you call the normal html helpers...)
And you can use asp.mvc to make your life easier:
public void MyMethod(object obj){
var dic=new System.Web.Routing.RouteValueDictionary(obj);
string param=dic["myparam"] as string;
}
Another way is convert anonymous data to json and then convert to c# json object. You can read all data with this way easily.
I would like to replace "PKMvrMedsProductIssuesId" for something like x=>x.PKMvrMedsProductIssueId or anything that is not based on a string. Why? Because if the database people choose to rename the field my program would crash.
public static SelectList MvrMedsProductErrors(this SelectList Object)
{
MedicalVarianceEntities LinqEntitiesCtx = new MedicalVarianceEntities();
var ProductErrorsListBoxRaw =
(
from x in LinqEntitiesCtx.ViewLookUpProductIssuesErrorsNames
select x
);
Object = new SelectList(ProductErrorsListBoxRaw, "PKMvrMedsProductIssuesId", "MvrMedsProductIssuesErrorsNames");
return Object;
}
You're using a SelectList. In order to call that constructor, you must have a string. Any change we could propose will still result in a string (from somewhere) being passed into that constructor.
The good news is: that string can come from anywhere. It can come from config, from the database... where ever you like.
I don't know the exact context here (what exactly "PKMvrMedsProductIssuesId" is) , but you can for example use such helper method:
public static string GetPropertyAsString<T>(Expression<Func<T, object>> expression)
{
return GetPropertyInfo(expression).Name;
}
To use an expression to get string:
GetPropertyAsString<MyType>(x => x.MyTypeProperty);
('MyTypeProperty' is your 'PKMvrMedsProductIssuesId' any 'MyType' one of your types where you may have your property defined)
string thing = "etc";
thing = thing.GetName();
//now thing == "thing"
Is this even possible?
public static string GetName(this object obj)
{
return ... POOF! //should == "thing"
}
I agree #Reed's answer. However, if you REALLY want to achieve this functionality, you could make this work:
string thing = "etc";
thing = new{thing}.GetName();
The GetName extension method would simply use reflection to grab the name of the first property from the anonymous object.
The only other way would be to use a Lambda Expression, but the code would definitely be much more complicated.
No. At the point you're using it, the "name" would be "obj" - This could be retrieved (with debugging symbols in place) via MethodBase.GetCurrentMethod().GetParameters()[0].Name.
However, you can't retrieve the variable name from the calling method.
If you need the original variable name inside an extension method, I think it's best to do this:
thing.DoSomething(nameof(thing));
public static string DoSomething(this object obj, string name) {
// name == "thing"
}
New in C# 6 is nameof() which would replace the extension method entirely.
if (x == null) throw new ArgumentNullException(nameof(x));
WriteLine(nameof(person.Address.ZipCode)); // prints "ZipCode”
Somewhat related is the CallerMemberAttribute which will get the name of the method where the function was called. A useful comparison of the two methods, with examples relating to PropertyChanged events, also talks about the IL code generated (TL;DR: they're the same).