Is it possible to do the following (If so I can't seem to get it working.. forgoing constraints for the moment)...
If the type (because it's ommitted) is inferred, what's the problem?
private void GetGenericTableContent<T>(ref StringBuilder outputTableContent, T item)
{
outputTableContent.Append("<td>" + item.SpreadsheetLineNumbers + "</td>");
}
// 'item' is either DuplicateSpreadsheetRowModel class or SpreadsheetRowModel class
With the above code I get the following error:
'T' does not contain a definition for 'SpreadsheetLineNumbers' and no extension method 'SpreadsheetLineNumbers' accepting a first argument of type 'T' could be found (are you missing a using directive or an assembly reference?)
No. Generic types must be known at compile time.
Think about it for a minute, how could compiler know that it is guaranteed that type T
has specific property, namely SpreadsheetLineNumbers
? What if T
is of primitive type such as int
or object
?
What prevents us from calling the method like this: GetGenericTableContent(ref _, 999)
with T as int here ?
To fix it you could first add an interface that contains the property :
public interface MyInterface
{
string SpreadsheetLineNumbers { get; set; }
}
And let your class inherit from this interface:
public class MyClass : MyInterface
{
public string SpreadsheetLineNumbers { get; set; }
}
Then we use generic type constraints to let compiler know that the type T
derives from this interface and therefore it has to contain and implement all its members:
private void GetGenericTableContent<T>(ref StringBuilder outputTableContent, T item)
where T : IMyInterface // now compiler knows that type T has implemented SpreadsheetLineNumbers
{
outputTableContent.Append("<td>" + item.SpreadsheetLineNumbers + "</td>");
}
Actually it is possible if you know for sure that the generic T has the exact property, using the where (generic type constraint) for a specified class with where T : MyClass
.
For instance, if you have two entities Foo
and Boo
:
class Foo
{
public Guid Id {get; set;}
public DateTime CreateDate {get; set;}
public int FooProp {get; set;}
}
class Boo
{
public Guid Id {get; set;}
public DateTime CreateDate {get; set;}
public int BooProp {get; set;}
}
With a little refactor we can create a BaseClass
that will hold the common properties:
class BaseModel
{
public Guid Id {get; set;}
public DateTime CreateDate {get; set;}
}
And modify Foo
and Boo
to be:
class Boo : BaseModel
{
public int BooProp {get; set;}
}
class Foo : BaseModel
{
public int FooProp {get; set;}
}
And If you have a generic service with the constraint type of where T : BaseModel
the compiler will allow you to get or set the BaseModel
's properties.
Lets say that you want that for every entity (Foo
or Boo
) added to DB you will want to set the CreateDate
and Id
properties from code (and not from server default value):
public interface IGenericService<T>
{
void Insert(T obj);
}
public class GenericService<T> : IGenericService<T> where T : BaseModel
{
public void Insert(T obj)
{
obj.Id = Guid.NewGuid();
obj.CreateDate = DateTime.UtcNow;
this._repository.Insert(obj);
}
}
If you can't get make an interface for your type (or a common one between several types):
private void GetGenericTableContant<T>(ref StringBuilder outputTableContent, T item, Func<T, string> lineNumberAccessor)
{
outputTableContent.Append("<td>" + lineNumberAccessor(item) + "</td>");
}
Usage:
GetGenericTableContent(ref outputTableContent, item, x => x.SpreadsheetLineNumbers);
(Or you could just pass the SpreadSheetLineNumbers property if you don't really need the item reference in your method: void GetGenericTableContant<T>(ref StringBuilder outputTableContent, string lineNumbers)
)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With