Считайте следующее (в большой степени упрощенным) кодом:
public T Function<T>() {
if (typeof(T) == typeof(string)) {
return (T) (object) "hello";
}
...
}
Довольно абсурдно сначала бросить к object
, затем к T
. Но компилятор не имеет никакого способа знать, что предыдущий тест гарантировал T
имеет тип string
.
Что является самым изящным, идиоматическим способом достигнуть этого поведения в C# (который включает избавление от глупого typeof(T) == typeof(string)
, с тех пор T is string
не может использоваться)?
Приложение: в .NET нет никакого различия типа возврата, таким образом, Вы не можете сделать функциональную перегрузку для ввода строки (который, между прочим, просто пример, но одна причина, почему переопределение конца ассоциации в полиморфизме, например, UML, не может быть сделано в c#). Очевидно, следующее было бы большим, но оно не работает:
public T Function<T>() {
...
}
public string Function<string>() {
return "hello";
}
Конкретный Пример 1: Поскольку было несколько нападений к тому, что родовая функция, которая тестирует на определенные типы, не универсальна, я попытаюсь обеспечить более полный пример. Рассмотрите Квадратный Типом шаблон разработки. Здесь следует за отрывком:
public class Entity {
Dictionary<PropertyType, object> properties;
public T GetTypedProperty<T>(PropertyType p) {
var val = properties[p];
if (typeof(T) == typeof(string) {
(T) (object) p.ToString(this); // magic going here
}
return (T) TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(val);
}
}
Конкретный Пример 2: Рассмотрите шаблон разработки Интерпретатора:
public class Expression {
public virtual object Execute() { }
}
public class StringExpression: Expression {
public override string Execute() { } // Error! Type variance not allowed...
}
Теперь давайте использовать дженерики в, Выполняются, чтобы позволить вызывающей стороне вызывать тип возврата:
public class Expression {
public virtual T Execute<T>() {
if(typeof(T) == typeof(string)) { // what happens when I want a string result from a non-string expression?
return (T) (object) do_some_magic_and_return_a_string();
} else if(typeof(T) == typeof(bool)) { // what about bools? any number != 0 should be True. Non-empty lists should be True. Not null should be True
return (T) (object) do_some_magic_and_return_a_bool();
}
}
}
public class StringExpression: Expressiong {
public override T Execute<T>() where T: string {
return (T) string_result;
}
}
Необходимо использовать эту форму для передачи параметров стратегии слияния:
git merge -s recursive -Xtheirs # short options
git merge --strategy recursive --strategy-option theirs # long options
Также убедитесь, что ваша версия поддерживает -Xtheirs
, это совсем недавно (?)
Возможно, стоит попробовать FTP-компонент с открытым исходным кодом из здесь ... Я попробовал использовать Ftp WebRequest и мой опыт использования, что было отрицательным... медленно, тайм-аут, потому что совершенно естественно, что Ftp WebRequest работает через порт 80 вместо собственного порта 21 ... ситуация изменилась довольно резко, когда я использовал этот FTP-компонент, более универсальный и мощный...
Изменить: Как Джейкоб указал на мою очевидную ошибку и нелогичный взгляд на класс Ftp WebRequest, которые заставляют меня поверить, что происходит что-то странное и странное, и что он каким-то образом делает что-то через HTTP... Ну, Джейкоб должен иметь точку зрения... классический случай плохого соглашения об именовании в рамках... Спасибо Джейкобу!
Надеюсь, что это поможет, С уважением, Том.
-121--4950457-Хорошо, я сделал пробежку на ней с нескольких разных ракурсов и подошел коротко. Я должен был бы сделать вывод, что если ваша текущая реализация получит работу, вы должны взять победу и двигаться дальше. Мало каких-то выбросов тростника, что вы получили, это то, что вы получили.
Но компилятор не может знать что предыдущий тест, гарантированный T, введите последовательность.
Умм... Если я не ошибаюсь, дженерики - это просто код ген. компилятор генерирует соответствующий метод для каждого отдельного типа, найденного в вызывающих методах. Таким образом, компилятор знает аргумент типа для вызываемой перегрузки. Опять же; Если я не ошибаюсь.
Но в целом, я думаю, что вы неправильно используете дженерик в данном случае, из того, что я могу видеть, и, как заявили другие, есть более подходящие решения..... которые являются неназванными, если только вы не разносите код, который полностью определяет ваши требования.
только мои 2 песо...
Если вы выполняете эти типы проверок с помощью универсального метода, я бы переосмыслил ваш дизайн. Очевидно, что этот метод не совсем универсальный - если бы это было так, вам не потребовалась бы особая проверка типов ...
Ситуации, подобные этой, обычно можно обрабатывать более чисто с помощью редизайна. Одной из альтернатив часто является перегрузка соответствующего типа. Существуют и другие альтернативы дизайна, которые избегают поведения, зависящего от типа, например, предложение Ричарда Берга о передаче делегата .
using System;
using System.Collections.Generic;
using System.Linq;
namespace SimpleExamples
{
/// <summary>
/// Compiled but not run. Copypasta at your own risk!
/// </summary>
public class Tester
{
public static void Main(string[] args)
{
// Contrived example #1: pushing type-specific functionality up the call stack
var strResult = Example1.Calculate<string>("hello", s => "Could not calculate " + s);
var intResult = Example1.Calculate<int>(1234, i => -1);
// Contrived example #2: overriding default behavior with an alternative that's optimized for a certain type
var list1 = new List<int> { 1, 2, 3 };
var list2 = new int[] { 4, 5, 6 };
Example2<int>.DoSomething(list1, list2);
var list1H = new HashSet<int> { 1, 2, 3 };
Example2<int>.DoSomething<HashSet<int>>(list1H, list2, (l1, l2) => l1.UnionWith(l2));
}
}
public static class Example1
{
public static TParam Calculate<TParam>(TParam param, Func<TParam, TParam> errorMessage)
{
bool success;
var result = CalculateInternal<TParam>(param, out success);
if (success)
return result;
else
return errorMessage(param);
}
private static TParam CalculateInternal<TParam>(TParam param, out bool success)
{
throw new NotImplementedException();
}
}
public static class Example2<T>
{
public static void DoSomething(ICollection<T> list1, IEnumerable<T> list2)
{
Action<ICollection<T>, IEnumerable<T>> genericUnion = (l1, l2) =>
{
foreach (var item in l2)
{
l1.Add(item);
}
l1 = l1.Distinct().ToList();
};
DoSomething<ICollection<T>>(list1, list2, genericUnion);
}
public static void DoSomething<TList>(TList list1, IEnumerable<T> list2, Action<TList, IEnumerable<T>> specializedUnion)
where TList : ICollection<T>
{
/* stuff happens */
specializedUnion(list1, list2);
/* other stuff happens */
}
}
}
/// I confess I don't completely understand what your code was trying to do, here's my best shot
namespace TypeSquarePattern
{
public enum Property
{
A,
B,
C,
}
public class Entity
{
Dictionary<Property, object> properties;
Dictionary<Property, Type> propertyTypes;
public T GetTypedProperty<T>(Property p)
{
var val = properties[p];
var type = propertyTypes[p];
// invoke the cast operator [including user defined casts] between whatever val was stored as, and the appropriate type as
// determined by the domain model [represented here as a simple Dictionary; actual implementation is probably more complex]
val = Convert.ChangeType(val, type);
// now create a strongly-typed object that matches what the caller wanted
return (T)val;
}
}
}
/// Solving this one is a straightforward application of the deferred-execution patterns I demonstrated earlier
namespace InterpreterPattern
{
public class Expression<TResult>
{
protected TResult _value;
private Func<TResult, bool> _tester;
private TResult _fallback;
protected Expression(Func<TResult, bool> tester, TResult fallback)
{
_tester = tester;
_fallback = fallback;
}
public TResult Execute()
{
if (_tester(_value))
return _value;
else
return _fallback;
}
}
public class StringExpression : Expression<string>
{
public StringExpression()
: base(s => string.IsNullOrEmpty(s), "something else")
{ }
}
public class Tuple3Expression<T> : Expression<IList<T>>
{
public Tuple3Expression()
: base(t => t != null && t.Count == 3, new List<T> { default(T), default(T), default(T) })
{ }
}
}
Можете ли вы использовать здесь как
?
T s = "hello" as T;
if(s != null)
return s;
Я не могу придумать "элегантного" способа сделать это. Как вы говорите, компилятор не может знать, что условное выражение гарантирует, что типом T является строка
. В результате он должен предположить, что, поскольку нет универсального способа преобразования из строки
в T, это ошибка. объект
в T может завершиться успешно, поэтому компилятор допускает это.
Я не уверен, что мне нужен элегантный способ выразить это. Хотя я вижу, где в некоторых ситуациях может потребоваться такая явная проверка типов, я думаю, что я бы хотел, чтобы это было громоздко, потому что это действительно что-то вроде взлома. И я бы хотел, чтобы оно выделялось: «Эй! Я здесь что-то странное делаю!»