So playing with my own test Dependency Injector class. (yeah tons out there but this is just for fun)
Works decent but I don't know how to get the correct constructor based on the Interface passed in.
internal class DiContainer
{
private readonly Dictionary<Type, RegistryRecord> registry = new Dictionary<Type, RegistryRecord>();
private static DiContainer instance;
private DiContainer()
{
}
public static DiContainer GetInstance()
{
return instance ??= new DiContainer();
}
public void Register<T, C>() where C : class, T
{
registry.Add(typeof(T), new RegistryRecord
{
InterfaceType = typeof(T),
ConcreteType = typeof(C),
IsSingleTon = false
});
}
public void Register<C>() where C : class
{
Register(typeof(C));
}
public void Register(Type t)
{
registry.Add(t, new RegistryRecord
{
InterfaceType = t,
ConcreteType = t,
IsSingleTon = false
});
}
public void RegisterSingleton<T, C>(C instance = null) where C : class, T
{
registry.Add(typeof(T), new RegistryRecord
{
InterfaceType = typeof(T),
ConcreteType = typeof(C),
IsSingleTon = true,
Instance = instance
});
}
public T Get<T>()
{
return (T) Get(typeof(T));
}
public object Get(Type t)
{
ConstructorInfo constructor;
RegistryRecord r = null;
if (t.IsInterface && registry.ContainsKey(t))
{
r = registry[t];
if (r.IsSingleTon && r.Instance != null) return r.Instance;
constructor = r.ConcreteType.GetConstructors(BindingFlags.Instance | BindingFlags.Public)[0];
}
else
{
//todo how do we select the correct constructor?
constructor = t.GetConstructors(BindingFlags.Instance | BindingFlags.Public)[0];
}
var parameters = constructor.GetParameters();
//recurse to build dependency chain
var objects = parameters.Select(parameter => Get(parameter.ParameterType)).ToList();
var obj = constructor.Invoke(objects.ToArray());
if (r != null && r.IsSingleTon)
{
r.Instance = obj;
}
return obj;
}
}
internal class RegistryRecord
{
public Type InterfaceType { get; set; }
public Type ConcreteType { get; set; }
public object Instance { get; set; }
public bool IsSingleTon { get; set; }
}
So the problem is
constructor = t.GetConstructors(BindingFlags.Instance | BindingFlags.Public)[0];
I am just assuming the first constructor which is awful. But I have the definition of the interface I could be using.
How do I get the parameters of my interface and check them against the constructor?
Would like to select a constructor that matches the interface, or at least partially matches optimally.
edit
An example
using System;
namespace DITest
{
internal class Program
{
private static void Main(string[] args)
{
var di = DiContainer.GetInstance();
//Register classes / interfaces
di.Register<IPhoneResolver, PhoneResolver>();
di.Register<Customer>();
//Get class where dependency should be injected
var x = di.Get<Customer>();
Console.WriteLine(x.resolver.Name);
Console.Read();
}
}
public class Customer
{
//Remove this and everything is ok. Because we select the first one not the right one
public Customer()
{
}
public Customer(IPhoneResolver resolver)
{
this.resolver = resolver;
}
public IPhoneResolver resolver { get; set; }
}
public interface IPhoneResolver
{
string Name { get; set; }
bool DoesSomething();
}
public class PhoneResolver : IPhoneResolver
{
public string Name { get; set; } = "test";
public bool DoesSomething()
{
return true;
}
}
}
So because the first constructor is null there is an issue.
I need a way to resolve the correct constructor. I have the interface via the RegistryRecord and (type) InterfaceType. I need to find a way to get a constructor that matches that types parameters.
Related
I will plot an example to illustrate my behind the scene issues.
Let say I have this base generic class :
public abstract class ContainerBase<T>
{
Guid Id {get; init;}
IList<T> Items {get; set;}
bool IsLeaf {get; set;} = false;
/// omitted constructors and so
}
Then I have a whole bunch (undefined number) of concrete Container class that have another ContainerBase<...> as the T type argument :
public class RootContainer : ContainerBase<ChildContainer1>
{...}
public class ChildContainer1: ContainerBase<ChildContainer2>
{...}
public class ChildContainer2: ContainerBase<ChildContainer3>
{...}
...
public class ChildContainerNminus1: ContainerBase<ChildContainerN>
{...}
public class ChildContainerN: ContainerBase<int> // the recursion end here.
{
...
IsLeaf = true;
}
Now let say I have an AddContainer method from an Utility class and have access to the RootContainer object (a singleton for example) that is fully populated of recursive sub containers.
public static class ContainerUtility
{
// What is the Type of the recursive currentContainer ?
public static ContainerBase<T> FindContainer<T>(Guid id, ContainerBase<?> currentContainer)
{
if(currentContainer.Id == id)
return currentContainer;
if(currentContainer.IsLeaf) return default;
foreach(var item in currentContainer.Items)
{
var potential = FindContainer(id, item);
if(potential != default) return potential;
}
return default;
}
public static bool AddContainer<T>(ContainerBase<T> container, Guid parentId)
{
// potential should be of container's parent type (ContainerBase<"T-1">)
// but how to "bybass" an expected type parameter as I cannot know it ?
var potential = FindContainer<?>(parentId, RootContainer.Instance)
if(potential != default && potential is ContainerBase<?>)
{
potential.Items.Add(container)
return true;
}
return false;
}
}
You see, my issue is that I have a base type ContainerBase that is convenient for recursive search as all subClasses allow access to Items list to pursue recursion.
But at each step of the recursion it is a different actual type of ContainerBase<?>.
So I cannot perform cast on the method argument.
maybe use a top level interface that expose a List<object> Items ? Not sure that will end up good.
Bellow was my intermediate mid-solution on my issue.
I'll keep it for the record or erase it if you request it to clarify this response.
Ok I get something more interesting now. I would like to have your criticism of this solution I end up with :
Mainly I abstracted a way higher with a non generic interface to avoid my issue described in OP.
The Interface
public interface IContainer
{
int TAG { get; init; } // usefull for logging purpose
string Name { get; }
Guid Id { get; init; }
public bool IsLeaf { get;}
IList<IContainer>? GetContainers();
void SetContainers(List<IContainer> value);
}
The base class
public abstract class ContainerBase<T> : IContainer where T : IContainer
{
public int TAG { get; init; }
public Guid Id { get; init; }
private IList<IContainer>? _containers = new List<IContainer>();
public IList<T> Items { get; set; } = new List<T>();
public bool IsLeaf => this.GetType() == typeof(T);
public string Name => this.GetType().Name + "_" + TAG;
public ContainerBase(Guid id)
{
TAG = ContainerUtils.ContainerCount++;
Id=id;
}
public ContainerBase()
{
TAG = ContainerUtils.ContainerCount++;
Id = Guid.NewGuid();
}
public IList<IContainer>? GetContainers()
{
if(Items == null) return null;
if(_containers == null || !_containers.Any())
_containers = Items.Where(x => x!=null).Select(x => (IContainer)x!).ToList();
return _containers;
}
public void SetContainers(List<IContainer> value)
{
Items = new List<T>();
foreach(var item in value)
{
if (item is T)
Items.Add((T)item);
}
}
}
The concrete classes
internal class RootContainer : ContainerBase<Child1Container>
{
public RootContainer(Guid id) : base(id)
{
}
public RootContainer() : base()
{
}
}
The intermediate containers are the same only class name change (X = 1 to 3 in my test case)
internal class ChildXContainer : ContainerBase<ChildX+1Container>
{
public ChildXContainer(Guid id) : base(id)
{
}
public ChildXContainer() : base()
{
}
}
The leaf class (end point of my chained containers classes recursion).
internal class LeafContainer : ContainerBase<LeafContainer>
{
public int IntItem { get; set; }
public LeafContainer() : base()
{
}
public LeafContainer(Guid id) : base(id)
{
}
}
Do note I'm using a trick to detect if a ContainerBase<T> concrete implementation is a leaf or not :
If such classes are leaves then they have to derive from ContainerBase<> of themselves.
Kind like the CRTP syntax, but without its meaning.
So I'm not fully satisfied of this trick, but better than my previous attempt so far.
The Utility class
internal static class ContainerUtils
{
public static int ContainerCount = 0;
public static Guid IdToSearch {
get
{
if(!AllIds.Any())
return Guid.Empty;
return AllIds[new Random().Next(AllIds.Count - 1)];
}
//set { IdToSearch = value; }
}
public static List<Guid> AllIds { get; set; } = new();
private static RootContainer _root = BuildContainers();
public static RootContainer Root => _root;
private static RootContainer BuildContainers()
{
LeafContainer Leaf = new LeafContainer();
Child3Container Child3 = new Child3Container();
Child2Container Child2 = new Child2Container();
Child1Container Child1 = new Child1Container();
RootContainer Root = new RootContainer();
Root.Items.Add(Child1);
Child1.Items.Add(Child2);
Child2.Items.Add(Child3);
Child3.Items.Add(Leaf);
Leaf.IntItem = 12;
AllIds.Add(Root.Id);
AllIds.Add(Child1.Id);
AllIds.Add(Child2.Id);
AllIds.Add(Child3.Id);
AllIds.Add(Leaf.Id);
return Root;
}
private static IContainer? _GetSubContainer(this IContainer container, int index)
=> (container == null ||
container.GetContainers() == null ||
index >= container.GetContainers()!.Count) ? null : container.GetContainers()![index];
public static string ContainersToString()
=> ContainersToString(Root);
public static string ContainersToString(IContainer? fromContainer)
{
if (fromContainer == null) return string.Empty;
int i = 0;
string tab = " ";
string res = "";
while(fromContainer != null)
{
res += tab.Repeat(i) + "+" + fromContainer.Name??"NULL";
res += "\n";
i++;
fromContainer = _GetSubContainer(fromContainer, 0);
}
return res;
}
public static IContainer? SearchContainer(Guid id)
=> SearchContainer(id, Root);
public static IContainer? SearchContainer(Guid id, IContainer? fromContainer)
{
if (fromContainer == null) return null;
if (fromContainer.Id == id)
return fromContainer;
if (fromContainer.IsLeaf)
return null;
return SearchContainer(id, fromContainer._GetSubContainer(0));
}
public static bool SetItemToContainer(Guid id, IContainer newContainer)
{
var container = SearchContainer(id);
if(container == null) return false;
if (container._GetSubContainer(0) == null || (container.GetContainers()![0].GetType() != newContainer.GetType()))
return false;
container.GetContainers()![0] = newContainer;
return true;
}
}
The Program and its output
Console.WriteLine(ContainerUtils.ContainersToString());
IContainer newChild2 = new Child2Container();
Console.WriteLine("Child2's Name : " + ContainerUtils.SearchContainer(ContainerUtils.AllIds[2])?.Name ?? "NULL");
Console.WriteLine("New Child2's Name : " + newChild2.Name);
ContainerUtils.SetItemToContainer(ContainerUtils.AllIds[1], newChild2);
Console.WriteLine(ContainerUtils.ContainersToString());
Output
+RootContainer_4
+Child1Container_3
+Child2Container_2
+Child3Container_1
+LeafContainer_0
Child2's Name : Child2Container_2
New Child2's Name : Child2Container_5
+RootContainer_4
+Child1Container_3
+Child2Container_5
OLD answer
I created a new test project with a simpler version of my OP one.
Here is what I ended, and yep found using an interface as a "workish" solution (I'm not completely satisfied).
Let me know what you think of it please.
The interface without generic parameter :
public interface IContainer
{
string Name => this.GetType().Name;
public Guid Id { get; init; }
public IContainer? Item { get; set; }
public bool IsLeaf => Id == Guid.Empty;
}
The base abstract class with the generic parameter :
public abstract class ContainerBase<T> : IContainer where T : IContainer
{
public Guid Id { get; init; }
public T? Item { get; set; }
IContainer? IContainer.Item { get => Item; set => Item = (T)value; }
public ContainerBase(Guid id)
{
Id=id;
}
public ContainerBase()
{
Id = Guid.NewGuid();
}
}
The starting concrete Container class
internal class RootContainer : ContainerBase<Child1Container>
{
public RootContainer(Guid id) : base(id)
{
}
public RootContainer() : base()
{
}
}
The child Container concrete classes.
In my project there is Child1Container, Child2Container and Child3Container. I only display Child1Container here. The other are the same except class name.
internal class Child1Container : ContainerBase<Child2Container>
{
public Child1Container(Guid id) : base(id)
{
}
public Child1Container() : base()
{
}
}
The endind Container (noted Leaf here) :
This is where I found the code most ugly..
internal class LeafContainer : IContainer
{
public int IntItem { get; set; }
public Guid Id { get; init ; }
/// Meh, would be nice to avoid this.
public IContainer? Item { get => null; set => Item = null; }
public LeafContainer()
{
Id = Guid.Empty;
}
}
My utility class :
internal static class ContainerUtils
{
public static Guid IdToSearch { get; set; }
private static RootContainer _root = BuildContainers();
public static RootContainer Root => _root;
private static RootContainer BuildContainers()
{
LeafContainer Leaf = new LeafContainer();
Child3Container Child3 = new Child3Container();
Child2Container Child2 = new Child2Container();
Child1Container Child1 = new Child1Container();
RootContainer Root = new RootContainer();
Root.Item = Child1;
Child1.Item = Child2;
Child2.Item = Child3;
Child3.Item = Leaf;
Leaf.IntItem = 12;
IdToSearch = Root.Id;
return Root;
}
public static IContainer? SearchContainer(Guid id)
=> SearchContainer(id, Root);
public static IContainer? SearchContainer(Guid id, IContainer? fromContainer)
{
if (fromContainer == null) return null;
if(fromContainer.Id == id)
return fromContainer;
if(fromContainer.IsLeaf)
return null;
return SearchContainer(id, fromContainer.Item);
}
}
Finally my Program :
using TestRecursiveGenerics;
var res = ContainerUtils.SearchContainer(ContainerUtils.IdToSearch);
Console.WriteLine("Searching IContainer's Id, and we found : "+ res?.Name ?? "NULL");
I use Activator.CreateInstance() like below
public class EntityBase
{
public EntityBase()
{
}
private readonly IServiceProvider serviceProvider;
public EntityBase(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
Type propertyType = GetPropertyType("ORMLawEntity");
Type type = propertyType.GetGenericArguments().Single();
object obj = Activator.CreateInstance(type);
}
to create the below Entity:
public class ORMLawEntity : EntityBase
{
private readonly ILazyLoader _lazyLoader;
[JsonConstructor]
public ORMLawEntity()
{
base.PrimaryKey = "idLaw";
base.EntityName = "Law";
}
public ORMLawEntity(ILazyLoader lazyLoader)
{
base.PrimaryKey = "idLaw";
base.EntityName = "Law";
_lazyLoader = lazyLoader;
}
[Key]
public long idLaw { set; get; }
private ORMCatalogValueEntity _LawTypeCIDEntity;
public virtual ORMCatalogValueEntity LawTypeCIDEntity
{
set { _LawTypeCIDEntity = value; }
get {
return _lazyLoader.Load(this, ref _LawTypeCIDEntity);
}
}
}
but when I use Activator.CreateInstance(type) default constructor executed and I don't have lazyloader. but I want to fill lazyLoader for LawTypeCIDEntity property.
Add the params inside the CreateInstance method. The appropriate constructor will implicitly be resolved.
Activator.CreateInstance(type, someLazyLoader);
If you want to inject it from a DI container
public class EntityBase
{
private readonly ILazyLoader lazyLoader;
public EntityBase()
{
}
public EntityBase(ILazyLoader lazyLoader)
{
this.lazyLoader = lazyLoader;
}
public ORMLawEntity CreateORMLawEntity()
{
return (ORMLawEntity) Activator.CreateInstance(type, lazyLoader);
}
}
I am trying to create a class hierarchy where a BaseClass.GetCopy() method would provide copies of the same runtime subclass, with the same readonly ID property (passed via constructor), and with every public writeable property copied.
I got the code below, but the tests don't pass, since I didn't know how to implement reflection-based property copying. Also, the constructors in base class became duplicated (error-prone, IMO), and the parameterized one is public, which is not a good thing in this case, since I don't want the client code to be able to set Ids explicitly.
My questions are stated as comments in the code below:
[TestFixture]
public class RepoItemTests
{
[Test]
public void CloneHasSameId()
{
var one = new ConcreteRepoItemName();
var two = one.GetCopy();
Assert.AreEqual(one.Id, two.Id);
}
[Test]
public void CloneIsSubclassInstance()
{
var one = new ConcreteRepoItemAge();
var two = one.GetCopy();
Assert.IsInstanceOf<ConcreteRepoItemAge>(two);
}
[Test]
public void ChangingCloneNameDoesntChangeOriginalAge()
{
var one = new ConcreteRepoItemName() { Name = "original" };
var two = one.GetCopy() as ConcreteRepoItemName;
Assert.AreEqual(one.Name, two.Name);
two.Name = "modified";
Assert.AreNotEqual(one.Name, two.Name);
}
[Test]
public void ChangingCloneAgeDoesntChangeOriginalAge()
{
var one = new ConcreteRepoItemAge() { Age = 22 };
var two = one.GetCopy() as ConcreteRepoItemAge;
Assert.AreEqual(one.Age, two.Age);
two.Age = 33;
Assert.AreNotEqual(one.Age, two.Age);
}
}
public class ConcreteRepoItemName : AbstractRepoItem<ConcreteRepoItemName>
{
public ConcreteRepoItemName() : base() { }
// I don't want the constructor below to be public
public ConcreteRepoItemName(Guid id) : base(id) { }
public string Name { get; set; }
}
public class ConcreteRepoItemAge : AbstractRepoItem<ConcreteRepoItemAge>
{
public ConcreteRepoItemAge() : base() { }
// I don't want the constructor below to be public
public ConcreteRepoItemAge(Guid id) : base(id) { }
public decimal Age { get; set; }
}
public abstract class AbstractRepoItem<T> where T : AbstractRepoItem<T>, new()
{
public AbstractRepoItem()
{
Id = Guid.NewGuid();
}
// I don't want the constructor below to be public
protected AbstractRepoItem(Guid id)
{
Id = id;
}
public Guid Id { get; private set; }
public T GetCopy()
{
var clone = Activator.CreateInstance(typeof(T), new object[] { Id }) as T;
/// HOW DO I COPY RUNTIME PROPERTIES HERE VIA REFLECTION?
return clone;
}
}
I have created a method that can serve your propose.
The idea is to iterate throw all the fields in the object and create a copy of it that way you can make your evaluations for testing.
public static T Copy<T>(T obj)
{
if (obj == null) throw new ArgumentNullException("obj");
Type Typeobj = obj.GetType();
var ResultObj = Activator.CreateInstance(Typeobj);
Type ResultObjType = ResultObj.GetType();
foreach (var field in Typeobj.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance))
{
FieldInfo f = ResultObjType.GetField(field.Name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
f.SetValue(ResultObj, field.GetValue(obj));
}
return (T) ResultObj;
}
I've created a Generic Class to parse some data into another instance of a class (MyClass1). Since MyClass1 has only built-in C# types, my GenericMethod works fine. The problem starts to grow when MyClass1 has another MyClass2 property and I still want to invoke my GenericMethod to parse my data.
I can't trigger my Generic Class method inside its scope since I need to change the type of T. Is there any way to solve this problem?
public class MyClass1
{
public int MyIntProperty { get; set; }
public string MyStringProperty { get; set; }
public MyClass2 MyClass2Property { get; set; }
}
public class MyClass2
{
public int MyOtherIntProperty { get; set; }
public string MyOtherStringProperty { get; set; }
public bool MyOtherBoolProperty { get; set; }
}
public class MyGenericClass<T> where T : class
{
public static T MyGenericMethod()
{
T o = (T)Activator.CreateInstance(typeof(T));
PropertyInfo[] pi = typeof(T).GetProperties();
for(int i = 0; i < pi.Count(); i++)
{
if(pi[i].Name == "MyClass2Property")
{
//How to proceed ?
MyGenericClass<???>.MyGenericMethod();
}
else
{
pi[i].SetValue(o, Convert.ChangeType(someValue, pi[i].PropertyType), null);
}
}
}
}
public static void Main(string[] args)
{
MyClass1 mc1 = MyGenericClass<MyClass1>.MyGenericMethod();
//Do something with mc1
}
You can look at this post
and maybe try something like this
public static class MyGenericClass<T> where T : class
{
public static T MyGenericMethod()
{
T o = (T)Activator.CreateInstance(typeof(T));
PropertyInfo[] pi = typeof(T).GetProperties();
for(int i = 0; i < pi.Count(); i++)
{
if(pi[i].Name == "MyClass2Property")
{
//How to proceed ?
Type t = typeof (MyGenericClass<>);
Type genericType = t.MakeGenericType(new System.Type[] { pi[i].PropertyType });
var c = Activator.CreateInstance(genericType);
dynamic mgm = Convert.ChangeType(c, genericType);
mgm.MyGenericMethod();
}
else
{
pi[i].SetValue(o, Convert.ChangeType(someValue, pi[i].PropertyType), null);
}
}
}
Depending on your needs, you could also define some additional meta information about the property indicating what you would like to do with it if found.
Building on others' comments and answers, here is what I came up with including an attribute decoration that allows you to dynamically build objects and has the following enhancements:
Properties can be named anything you want
No need for if statements as new properties are added.
No need for the MyGenericMethod method to ever change.
Additional meta-information can be added to the custom attribute to add further customization in the future.
Objects can be nested as deeply as needed.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Dynamic;
public class MyClass1 {
public int MyIntProperty { get; set; }
public string MyStringProperty { get; set; }
[MyCustom()]
public MyClass2 MyClass2Property { get; set; }
}
public class MyClass2 {
public int MyOtherIntProperty { get; set; }
public string MyOtherStringProperty { get { return "oooh, fancy"; } set {} }
public bool MyOtherBoolProperty { get; set; }
}
public static class MyGenericClass<T> where T : class {
public static T MyGenericMethod() {
T o = (T)Activator.CreateInstance(typeof(T));
PropertyInfo[] pi = typeof(T).GetProperties();
for (int i = 0; i < pi.Count(); i++) {
if (pi[i].GetCustomAttributes(true).Any() && pi[i].GetCustomAttributes(true).Where((x) => x is MyCustomAttribute).Any()) {
//How to proceed ?
var c = Activator.CreateInstance(pi[i].PropertyType);
Type t = typeof(MyGenericClass<>);
Type genericType = t.MakeGenericType(new System.Type[] { pi[i].PropertyType });
MethodInfo m = genericType.GetMethod(MethodInfo.GetCurrentMethod().Name);
c = m.Invoke(null, null);
pi[i].SetValue(o, c, null);
} else {
//Normal property assignment.
}
}
return o;
}
}
public class Program {
public static void Main(string[] args) {
MyClass1 mc1 = MyGenericClass<MyClass1>.MyGenericMethod();
//Do something with mc1
Console.WriteLine(mc1.MyClass2Property.MyOtherStringProperty);
Console.ReadLine();
}
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple=false)]
public class MyCustomAttribute : Attribute {
}
I tweaked this so it can be run as is.
Edit:
I also changed the code to invoke the method being called on itself to avoid a "magic string".
I've been searching for a while now and tested several methods, but i didn't find the answer i was looking for. I'll try to explain.
I have an object with several fields/properties. These properties have custom attributes.
What i want is to get the custom attribute from a specific propertie without all the knowlege of the object.
The are the base classes
// FieldAttr has a public Text propery
public class TestObject
{
// Declare fields
[FieldAttr("prop_testfld1")]
public FLDtype1 testfld1 = new FLDtype1();
[FieldAttr("prop_testfld2")]
public FLDtype2 testfld2 = new FLDtype2();
[FieldAttr("prop_testfld3")]
public FLDtype1 testfld3;
}
public class FLDtype1
{
public string Value { get; set; }
}
public class FLDtype2
{
public Guid Value { get; set; }
}
public sealed class FieldAttr: System.Attribute
{
private string _txt;
public EntityFieldType(string txt)
{
this._text = txt;
}
public string Text { get { return this._text; } }
}
And i want to be able to do this in my application:
static void Main(string[] args)
{
TestObject test = new TestObject();
// (Option 1: preferred)
Console.WriteLine(test.testfld1.getFieldAttr().Text);
// (Option 2)
Console.WriteLine(test.getFieldAttr(test.testfld1).Text);
}
Is this possible? I've seen methods to get custom attribute values from all properties/fields of an object, but not for a specific field.
I've got a working method to get custom attribute from an enum, but wasn't able to recreate it for object fields/properties. This is because i couldn't get the name of the field i was trying to explore, because (for example) test.testfld1.ToString() give's me "ns.FLDtype1".
Looking forward for the answer :)
(and excuse my english)
Yes it is possible:
public static class Extensions
{
public static FieldAttr GetFieldAttr(
this TestObject source,
Expression<Func<TestObject,object>> field)
{
var member = field.Body as MemberExpression;
if (member == null) return null; // or throw exception
var fieldName = member.Member.Name;
var test = typeof (TestObject);
var fieldType = test.GetField(fieldName);
if (fieldType != null)
{
var attribute = fieldType.GetCustomAttribute<FieldAttr>();
return attribute;
}
return null;
}
}
Usage:
TestObject test = new TestObject();
var attr = test.GetFieldAttr(x => x.testfld3);
if(attr != null) Console.WriteLine(attr.Text);
Here is the fiddle
After another day of trial and error I decided to make use of Selman22 answer with a little modification. This is code I created:
public class TestObject : iTestObject
{
// Declare fields
[FieldAttr("prop_testfld1")]
public FLDtype1 testfld1 = new FLDtype1();
[FieldAttr("prop_testfld2")]
public FLDtype2 testfld2 = new FLDtype2();
[FieldAttr("prop_testfld3")]
public FLDtype1 testfld3;
}
public class FLDtype1 : iField
{
public string Value { get; set; }
}
public class FLDtype2 : iField
{
public Guid Value { get; set; }
}
public sealed class FieldAttr: System.Attribute
{
private string _txt;
public FieldAttr(string txt)
{
this._txt = txt;
}
public string Text { get { return this._txt; } }
}
public interface iField { }
public interface iTestObject { }
public static class Extensions
{
public static FieldAttr GetFieldAttr<T>(this T source, Expression<Func<iField>> field) where T : iTestObject
{
// Get member body. If no body present, return null
MemberExpression member = (MemberExpression)field.Body;
if (member == null) { return null; }
// Get field info. If no field info present, return null
FieldInfo fieldType = typeof(T).GetField(member.Member.Name);
if (fieldType == null) { return null; }
// Return custom attribute
return fieldType.GetCustomAttribute<FieldAttr>();
}
}
Usage:
public class Program
{
public static void Main()
{
TestObject test = new TestObject();
Console.WriteLine(test.GetFieldAttr(() => test.testfld1).Text);
Console.WriteLine(test.GetFieldAttr(() => test.testfld2).Text);
Console.WriteLine(test.GetFieldAttr(() => test.testfld3).Text);
}
}
Uses:
using System;
using System.Linq;
using System.Reflection;
using System.Linq.Expressions;
I have implemented interfaces to protect the GetFieldAttr method
#Sulman22: Thnx for the response!