[AttributeUsage(AttributeTargets.Method,AllowMultiple=true)]
public class MethodId : Attribute
{
private int mId;
public MethodId(int mId)
{
this.mId = mId;
}
public int methodId
{
get { return this.mId; }
set { this.mId = value; }
}
}
public class Methods
{
[MethodId(1)]
public void square()
{ }
[MethodId(2)]
public void Notify()
{ }
}
How to access square() in main() or in any other class with the help of MethodId?
private static MethodInfo GetMethodInfo(int id)
{
return typeof(Methods).GetMethods().
Where(x => x.GetCustomAttributes(false).OfType<MethodId>().Count() > 0)
.Where(x => x.GetCustomAttributes(false).OfType<MethodId>().First().methodId == id)
.First();
}
And usage:
var methodInfo = GetMethodInfo(1);
methodInfo.Invoke(new Methods(), null);
NOTE:
This solution is only meant to display how to do it. Not omptimised for performance. Ideally you would cache the methodInfos.
Related
I am having an issue with Reflection, which I can't seem to find a solution for.
I have the following simple interface :
public interface IDataProperty<T>
{
public T Value { get; set; }
public int BytesCount();
public byte[] Serialize();
}
the struct which implements the interface above :
public struct IntProperty : IDataPropery<int>
{
private int _value;
public int Value { get => _value; set => _value = value; }
public int BytesCount()
{
return 4;
}
public byte[] Serialize()
{
return BitConverter.GetBytes(_value);
}
public IntDataProperty(int value) { _value = value; }
}
and a simple class to hold the values :
public class ValuesContainer
{
public IntProperty prop1;
public IntProperty prop2;
}
I am trying to call the Serialize() method on both prop1 and prop2 in my Processor class,
with no luck so far... :
public class Processor
{
public void ProccesData<T>(out T result) where T : ValuesContainer, new()
{
result = new T();
List<FieldInfo> dataFields = new List<FieldInfo>();
result.GetType().GetFields().ToList().ForEach(field => {
if(field.FieldType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDataProperty<>)))
{
dataFields.Add(field);
}
});
MethodInfo serializeMI = typeof(IDataProperty<>).GetMethod("Serialize");
foreach(FieldInfo field in dataFields)
{
Console.WriteLine(field.Name);
serializeMI.Invoke(field,null);
}
}
}
Running the code at this point gives me the following error :
'Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true.'
I am aware that I need to get somehow to the instance behind the field variable, but have no idea how to do it.
Does anyone know a good way of doing what i am trying to achieve using other methods, or only Reflection is the way to go, and if the latter - what solutions do I have ?
Thanks in advance to all of you.
After the great help #JeroenMostert provided in the comments , i have been able to solve the issue at its roots. As long as Jeroen haven't provided an answer to be accepted as correct , I will just show his solution , so this question gets marked as answered. Still, the benefit here goes to #JeroenMostert
internal class Program
{
static void Main(string[] args)
{
ValuesContainer container = new ValuesContainer();
Processor processor = new Processor();
processor.ProccesData(out container);
}
}
public interface IDataProperty<T>
{
public void Serialize();
}
public struct IntProperty : IDataProperty<int>
{
private int _value;
public int Value { get => _value; set => _value = value; }
public int BytesCount()
{
return 4;
}
public void Serialize()
{
Console.WriteLine("I have been invoked !" + this.GetHashCode());
}
}
public class ValuesContainer
{
public IntProperty prop1;
public IntProperty prop2;
}
public class Processor
{
public void ProccesData<T>(out T result) where T : ValuesContainer, new()
{
result = new T();
List<FieldInfo> dataFields = new List<FieldInfo>();
result.GetType().GetFields().ToList().ForEach(field => {
if (field.FieldType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDataProperty<>)))
{
dataFields.Add(field);
}
});
foreach (FieldInfo field in dataFields)
{
Console.WriteLine("Invoking 'Serialize' method on fieldName :" + field.Name);
field.GetValue(result).GetType().GetMethod("Serialize").Invoke(field.GetValue(result), null);
}
}
}
Below are my classes
public class CommonClassSource
{
public int Id {get;set;}
}
public class CommonClassDestination
{
public int Id {get;set;}
}
public class SourceClass
{
public CommonClassSource CommonSource {get;set;}
}
public class DestinationClass
{
public CommonClassDestination CommonDestination {get;set;}
}
Is there any way to add null-checking in config.Bind? For example: here, check if source.CommonSource is null
TinyMapper.Bind<SourceClass, DestinationClass>(config => {
config.Bind(source => source.CommonSource.Id,
target => target.CommonDestination.Id));
});
If all you wanted to have the case handled that it there isn't an issue when source.CommonSource is null, the following script should work for you.
static bool isSourceNull;
public static void Main()
{
Func<SourceClass,object> getSCI = GetSourceClassId;
Func<DestinationClass, object> getDCI = GetDestClassId;
TinyMapper.Bind<SourceClass, DestinationClass>(config => {
config.Bind(source=>getSCI,
target =>getDCI);
});
}
static object GetSourceClassId(SourceClass source)
{
isSourceNull = source.CommonSource == null;
if (isSourceNull) return -1;
else return source.CommonSource.Id;
}
static object GetDestClassId(DestinationClass destinationClass)
{
if (isSourceNull)
{
destinationClass.CommonDestination = null;
return -1;
}
else if (destinationClass.CommonDestination != null)
return destinationClass.CommonDestination.Id;
else return -1;
}
I am getting a compile error on the following code (where I call _Context.OuterMethod(this)):
Error 3 Argument 1: cannot convert from TestApp.Outer<T>.Inner<T> to TestApp.Outer<T>.Inner<T>
Given the relationship between the classes shouldn't this work?
public class Outer<T>
where T : struct
{
public Outer()
{
_InnerObject = new Inner<T>(this);
}
private void OuterMethod(Inner<T> myInnerObject)
{
/* do something */
}
private Inner<T> _InnerObject; public Inner<T> InnerObject
{
get { return _InnerObject; }
}
public class Inner<T>
where T : struct
{
Outer<T> _Context;
public Inner(Outer<T> theContext)
{
_Context = theContext;
}
bool _SomeProperty;
public bool SomeProperty
{
get { return _SomeProperty; }
set
{
if(value != _SomeProperty)
{
_SomeProperty = value;
_Context.OuterMethod(this);
}
}
}
}
}
Given the comments, this code now works as I expected
public class Outer<T>
where T : struct
{
public Outer()
{
_InnerObject = new Inner(this);
}
private void OuterMethod(Inner myInnerObject)
{
/* do something */
}
private Inner _InnerObject; public Inner InnerObject
{
get { return _InnerObject; }
}
public class Inner
{
Outer<T> _Context;
public Inner(Outer<T> theContext)
{
_Context = theContext;
}
bool _SomeProperty;
public bool SomeProperty
{
get { return _SomeProperty; }
set
{
if (value != _SomeProperty)
{
_SomeProperty = value;
_Context.OuterMethod(this);
}
}
}
}
}
How to mock this class in nUnit Tests?
public class OpenDataQuery: PagedQuery, IOpenDataQuery
{
private static Dictionary<string, SortItem> m_sortModes;
protected override Dictionary<string, SortItem> SortModes
{
get
{
if (m_sortModes == null)
{
m_sortModes = new Dictionary<string, SortItem>();
AddSortMode(m_sortModes, new SortItem(ObjectExtensions.GetNameFromExpression<OpenDataCategoriesModel, string>(m => m.Name), "Наименование ↑", true) { IsDefault = true });
AddSortMode(m_sortModes, new SortItem(ObjectExtensions.GetNameFromExpression<OpenDataCategoriesModel, string>(m => m.Name), "Наименование ↓"));
}
return m_sortModes;
}
}
public IEnumerable<OpenDataCategoriesModel> OpenDataCategories { get; set; }
public string OpenDataTags { get; set; }
}
and
public abstract class PagedQuery : IPagedQuery
{
private const int DEFAULT_PAGE = 1;
private const int DEFAULT_COUNT = 5;
private int? m_page;
private int? m_count;
private int? m_total;
private string m_sort;
public int? Page
{
get
{
if (m_page == null || m_page <= 0)
{
return DEFAULT_PAGE;
}
return m_page;
}
set { m_page = value; }
}
public int? Count
{
get
{
if (m_count == null || m_count <= 0)
{
return DEFAULT_COUNT;
}
return m_count;
}
set { m_count = value; }
}
public int? Total
{
get
{
if (m_total == null || m_total <= 0)
{
return 0;
}
return m_total;
}
set { m_total = value; }
}
public string SearchQuery { get; set; }
protected virtual Dictionary<string, SortItem> SortModes
{
get { return null; }
}
public string Sort
{
get
{
var sortMode = GetSortMode(m_sort);
if (sortMode == null)
{
var defaultSort = (from i in SortModes where i.Value.IsDefault select i).FirstOrDefault();
if (!string.IsNullOrWhiteSpace(defaultSort.Key))
{
return defaultSort.Key;
}
return (from i in SortModes select i.Key).First();
}
return m_sort;
}
set
{
m_sort = value;
}
}
protected void AddSortMode(Dictionary<string, SortItem> sortModes, SortItem sortItem)
{
sortModes.Add(
String.Format(
"{0}{1}",
sortItem.FieldName.ToLower(),
sortItem.Asc ? "asc" : "desc"
),
sortItem
);
}
private SortItem GetSortMode(string sort)
{
if (SortModes == null || string.IsNullOrWhiteSpace(sort) ||
!SortModes.ContainsKey(sort.ToLower()))
{
return null;
}
return SortModes[sort.ToLower()];
}
public IOrderBy GetOrderBy()
{
var item = GetCurrentSortItem();
if (item == null)
{
return null;
}
if (item.Asc)
{
return new OrderBy(item.FieldName);
}
return new OrderByDesc(item.FieldName);
}
public SortItem GetCurrentSortItem()
{
return GetSortMode(Sort);
}
public Dictionary<string, SortItem> GetSortItems()
{
return SortModes;
}
}
and
public interface IOpenDataQuery : IPagedQuery
{
string OpenDataTags { get; set; }
}
I have some service method, that used openDataQuery class in parameters and in unit test i am trying mock this class, but this doesn't work:
public partial class OpenDataQueryRepository : Mock<OpenDataQuery>
{
public OpenDataQueryRepository(MockBehavior mockBehavior = MockBehavior.Strict)
: base(mockBehavior)
{
var opendataQuery = new Mock<IOpenDataQuery>();
var pagedQuery = opendataQuery.As<IPagedQuery>();
this.Setup(p=>p.GetOpenDataCategoriesMain(pagedQuery.Object,outttl)).Returns(OpenDataCategories);
}
}
I know that i should use Moq.Protected() for protected methods, but i don't know how use it correctly in this case. Please help me.
UPDATE:
I am testing this controller:
public class ODOpenDataController : ODBaseController
{
private readonly IOpenDataProvider m_openDataProvider;
public ODOpenDataController(IOpenDataProvider openDataProvider)
{
m_openDataProvider = openDataProvider;
}
public ActionResult Index(OpenDataQuery query)
{
int total;
query.OpenDataCategories = m_openDataProvider.GetOpenDataCategoriesMain(query, out total)
query.Total = total;
return View(query);
}
}
Test:
[Test]
public void Index_Test()
{
var opendataController = new ODOpenDataController(new OpenDataRepository().Object);
var result = opendataController.Index(new OpenDataQuery()) as ViewResult;
var model = result.Model as OpenDataQuery;
Assert.IsTrue(model.OpenDataCategories.Count() == 1);
}
Here is a Basic Class with TheProperty in question:
class BasicClass {
public BasicClass() {
TheProperty = new Object();
Stamped = DateTime.Now;
}
public object TheProperty { get; set; }
public DateTime Stamped { get; private set; }
}
Here is the Basic List:
class BasicList {
private List<BasicClass> list;
public BasicList() {
list = new List<BasicClass>();
}
public BasicClass this[object obj] {
get { return list.SingleOrDefault(o => o.TheProperty == obj); }
}
public void Add(BasicClass item) {
if (!Contains(item.TheProperty)) {
list.Add(item);
}
}
public bool Contains(object obj) {
return list.Any(o => o.TheProperty == obj); // Picked this little gem up yesterday!
}
public int Count { get { return list.Count; } }
}
I'd like to add a class to BasicList that will return an array of items.
I could write it like this, using traditional C#:
public object[] Properties() {
var props = new List<Object>(list.Count);
foreach (var item in list) {
props.Add(item.TheProperty);
}
return props.ToArray();
}
...but how would I write that using a LINQ or Lambda query?
return list.Select(p=>p.TheProperty).ToArray()